Home » quartz-1.6.0 » org » quartz » [javadoc | source]

    1   
    2   /* 
    3    * Copyright 2004-2005 OpenSymphony 
    4    * 
    5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
    6    * use this file except in compliance with the License. You may obtain a copy 
    7    * of the License at 
    8    * 
    9    *   http://www.apache.org/licenses/LICENSE-2.0 
   10    *   
   11    * Unless required by applicable law or agreed to in writing, software 
   12    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
   13    * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
   14    * License for the specific language governing permissions and limitations 
   15    * under the License.
   16    * 
   17    */
   18   
   19   /*
   20    * Previously Copyright (c) 2001-2004 James House
   21    */
   22   package org.quartz;
   23   
   24   import java.util.Date;
   25   
   26   
   27   /**
   28    * <p>
   29    * A concrete <code>{@link Trigger}</code> that is used to fire a <code>{@link org.quartz.JobDetail}</code>
   30    * at a given moment in time, and optionally repeated at a specified interval.
   31    * </p>
   32    * 
   33    * @see Trigger
   34    * @see CronTrigger
   35    * @see TriggerUtils
   36    * 
   37    * @author James House
   38    * @author contributions by Lieven Govaerts of Ebitec Nv, Belgium.
   39    */
   40   public class SimpleTrigger extends Trigger {
   41   
   42       /*
   43        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   44        * 
   45        * Constants.
   46        * 
   47        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   48        */
   49   
   50       /**
   51        * <p>
   52        * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
   53        * situation, the <code>{@link SimpleTrigger}</code> wants to be fired
   54        * now by <code>Scheduler</code>.
   55        * </p>
   56        * 
   57        * <p>
   58        * <i>NOTE:</i> This instruction should typically only be used for
   59        * 'one-shot' (non-repeating) Triggers. If it is used on a trigger with a
   60        * repeat count > 0 then it is equivalent to the instruction <code>{@link #MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT}
   61        * </code>.
   62        * </p>
   63        */
   64       public static final int MISFIRE_INSTRUCTION_FIRE_NOW = 1;
   65   
   66       /**
   67        * <p>
   68        * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
   69        * situation, the <code>{@link SimpleTrigger}</code> wants to be
   70        * re-scheduled to 'now' (even if the associated <code>{@link Calendar}</code>
   71        * excludes 'now') with the repeat count left as-is.  This does obey the
   72        * <code>Trigger</code> end-time however, so if 'now' is after the
   73        * end-time the <code>Trigger</code> will not fire again.
   74        * </p>
   75        * 
   76        * <p>
   77        * <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
   78        * the start-time and repeat-count that it was originally setup with (this
   79        * is only an issue if you for some reason wanted to be able to tell what
   80        * the original values were at some later time).
   81        * </p>
   82        * 
   83        * <p>
   84        * <i>NOTE:</i> This instruction could cause the <code>Trigger</code>
   85        * to go to the 'COMPLETE' state after firing 'now', if all the
   86        * repeat-fire-times where missed.
   87        * </p>
   88        */
   89       public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = 2;
   90   
   91       /**
   92        * <p>
   93        * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
   94        * situation, the <code>{@link SimpleTrigger}</code> wants to be
   95        * re-scheduled to 'now' (even if the associated <code>{@link Calendar}</code>
   96        * excludes 'now') with the repeat count set to what it would be, if it had
   97        * not missed any firings.  This does obey the <code>Trigger</code> end-time 
   98        * however, so if 'now' is after the end-time the <code>Trigger</code> will 
   99        * not fire again.
  100        * </p>
  101        * 
  102        * <p>
  103        * <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
  104        * the start-time and repeat-count that it was originally setup with (this
  105        * is only an issue if you for some reason wanted to be able to tell what
  106        * the original values were at some later time).
  107        * </p>
  108        * 
  109        * <p>
  110        * <i>NOTE:</i> This instruction could cause the <code>Trigger</code>
  111        * to go to the 'COMPLETE' state after firing 'now', if all the
  112        * repeat-fire-times where missed.
  113        * </p>
  114        */
  115       public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = 3;
  116   
  117       /**
  118        * <p>
  119        * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
  120        * situation, the <code>{@link SimpleTrigger}</code> wants to be
  121        * re-scheduled to the next scheduled time after 'now' - taking into
  122        * account any associated <code>{@link Calendar}</code>, and with the
  123        * repeat count set to what it would be, if it had not missed any firings.
  124        * </p>
  125        * 
  126        * <p>
  127        * <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
  128        * to go directly to the 'COMPLETE' state if all fire-times where missed.
  129        * </p>
  130        */
  131       public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = 4;
  132   
  133       /**
  134        * <p>
  135        * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
  136        * situation, the <code>{@link SimpleTrigger}</code> wants to be
  137        * re-scheduled to the next scheduled time after 'now' - taking into
  138        * account any associated <code>{@link Calendar}</code>, and with the
  139        * repeat count left unchanged.
  140        * </p>
  141        * 
  142        * <p>
  143        * <i>NOTE:</i> Use of this instruction causes the trigger to 'forget'
  144        * the repeat-count that it was originally setup with (this is only an
  145        * issue if you for some reason wanted to be able to tell what the original
  146        * values were at some later time).
  147        * </p>
  148        * 
  149        * <p>
  150        * <i>NOTE/WARNING:</i> This instruction could cause the <code>Trigger</code>
  151        * to go directly to the 'COMPLETE' state if all fire-times where missed.
  152        * </p>
  153        */
  154       public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = 5;
  155   
  156       /**
  157        * <p>
  158        * Used to indicate the 'repeat count' of the trigger is indefinite. Or in
  159        * other words, the trigger should repeat continually until the trigger's
  160        * ending timestamp.
  161        * </p>
  162        */
  163       public static int REPEAT_INDEFINITELY = -1;
  164   
  165       /*
  166        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  167        * 
  168        * Data members.
  169        * 
  170        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  171        */
  172   
  173       private Date startTime = null;
  174   
  175       private Date endTime = null;
  176   
  177       private Date nextFireTime = null;
  178   
  179       private Date previousFireTime = null;
  180   
  181       private int repeatCount = 0;
  182   
  183       private long repeatInterval = 0;
  184   
  185       private int timesTriggered = 0;
  186   
  187       private boolean complete = false;
  188   
  189       /*
  190        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  191        * 
  192        * Constructors.
  193        * 
  194        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  195        */
  196   
  197       /**
  198        * <p>
  199        * Create a <code>SimpleTrigger</code> with no settings.
  200        * </p>
  201        */
  202       public SimpleTrigger() {
  203           super();
  204       }
  205   
  206       /**
  207        * <p>
  208        * Create a <code>SimpleTrigger</code> that will occur immediately, and
  209        * not repeat.
  210        * </p>
  211        */
  212       public SimpleTrigger(String name, String group) {
  213           this(name, group, new Date(), null, 0, 0);
  214       }
  215   
  216       /**
  217        * <p>
  218        * Create a <code>SimpleTrigger</code> that will occur immediately, and
  219        * repeat at the the given interval the given number of times.
  220        * </p>
  221        */
  222       public SimpleTrigger(String name, String group, int repeatCount,
  223               long repeatInterval) {
  224           this(name, group, new Date(), null, repeatCount, repeatInterval);
  225       }
  226   
  227       /**
  228        * <p>
  229        * Create a <code>SimpleTrigger</code> that will occur at the given time,
  230        * and not repeat.
  231        * </p>
  232        */
  233       public SimpleTrigger(String name, String group, Date startTime) {
  234           this(name, group, startTime, null, 0, 0);
  235       }
  236   
  237       /**
  238        * <p>
  239        * Create a <code>SimpleTrigger</code> that will occur at the given time,
  240        * and repeat at the the given interval the given number of times, or until
  241        * the given end time.
  242        * </p>
  243        * 
  244        * @param startTime
  245        *          A <code>Date</code> set to the time for the <code>Trigger</code>
  246        *          to fire.
  247        * @param endTime
  248        *          A <code>Date</code> set to the time for the <code>Trigger</code>
  249        *          to quit repeat firing.
  250        * @param repeatCount
  251        *          The number of times for the <code>Trigger</code> to repeat
  252        *          firing, use {@link #REPEAT_INDEFINITELY}for unlimitted times.
  253        * @param repeatInterval
  254        *          The number of milliseconds to pause between the repeat firing.
  255        */
  256       public SimpleTrigger(String name, String group, Date startTime,
  257               Date endTime, int repeatCount, long repeatInterval) {
  258           super(name, group);
  259   
  260           setStartTime(startTime);
  261           setEndTime(endTime);
  262           setRepeatCount(repeatCount);
  263           setRepeatInterval(repeatInterval);
  264       }
  265   
  266       /**
  267        * <p>
  268        * Create a <code>SimpleTrigger</code> that will occur at the given time,
  269        * fire the identified <code>Job</code> and repeat at the the given
  270        * interval the given number of times, or until the given end time.
  271        * </p>
  272        * 
  273        * @param startTime
  274        *          A <code>Date</code> set to the time for the <code>Trigger</code>
  275        *          to fire.
  276        * @param endTime
  277        *          A <code>Date</code> set to the time for the <code>Trigger</code>
  278        *          to quit repeat firing.
  279        * @param repeatCount
  280        *          The number of times for the <code>Trigger</code> to repeat
  281        *          firing, use {@link #REPEAT_INDEFINITELY}for unlimitted times.
  282        * @param repeatInterval
  283        *          The number of milliseconds to pause between the repeat firing.
  284        */
  285       public SimpleTrigger(String name, String group, String jobName,
  286               String jobGroup, Date startTime, Date endTime, int repeatCount,
  287               long repeatInterval) {
  288           super(name, group, jobName, jobGroup);
  289   
  290           setStartTime(startTime);
  291           setEndTime(endTime);
  292           setRepeatCount(repeatCount);
  293           setRepeatInterval(repeatInterval);
  294       }
  295   
  296       /*
  297        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  298        * 
  299        * Interface.
  300        * 
  301        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  302        */
  303   
  304       /**
  305        * <p>
  306        * Get the time at which the <code>SimpleTrigger</code> should occur.
  307        * </p>
  308        */
  309       public Date getStartTime() {
  310           return startTime;
  311       }
  312   
  313       /**
  314        * <p>
  315        * Set the time at which the <code>SimpleTrigger</code> should occur.
  316        * </p>
  317        * 
  318        * @exception IllegalArgumentException
  319        *              if startTime is <code>null</code>.
  320        */
  321       public void setStartTime(Date startTime) {
  322           if (startTime == null) {
  323               throw new IllegalArgumentException("Start time cannot be null");
  324           }
  325   
  326           Date eTime = getEndTime();
  327           if (eTime != null && startTime != null && eTime.before(startTime)) {
  328               throw new IllegalArgumentException(
  329                   "End time cannot be before start time");    
  330           }
  331   
  332           this.startTime = startTime;
  333       }
  334   
  335       /**
  336        * <p>
  337        * Get the time at which the <code>SimpleTrigger</code> should quit
  338        * repeating - even if repeastCount isn't yet satisfied.
  339        * </p>
  340        * 
  341        * @see #getFinalFireTime()
  342        */
  343       public Date getEndTime() {
  344           return endTime;
  345       }
  346   
  347       /**
  348        * <p>
  349        * Set the time at which the <code>SimpleTrigger</code> should quit
  350        * repeating (and be automatically deleted).
  351        * </p>
  352        * 
  353        * @exception IllegalArgumentException
  354        *              if endTime is before start time.
  355        */
  356       public void setEndTime(Date endTime) {
  357           Date sTime = getStartTime();
  358           if (sTime != null && endTime != null && sTime.after(endTime)) {
  359               throw new IllegalArgumentException(
  360                       "End time cannot be before start time");
  361           }
  362   
  363           this.endTime = endTime;
  364       }
  365   
  366       /**
  367        * <p>
  368        * Get the the number of times the <code>SimpleTrigger</code> should
  369        * repeat, after which it will be automatically deleted.
  370        * </p>
  371        * 
  372        * @see #REPEAT_INDEFINITELY
  373        */
  374       public int getRepeatCount() {
  375           return repeatCount;
  376       }
  377   
  378       /**
  379        * <p>
  380        * Set the the number of time the <code>SimpleTrigger</code> should
  381        * repeat, after which it will be automatically deleted.
  382        * </p>
  383        * 
  384        * @see #REPEAT_INDEFINITELY
  385        * @exception IllegalArgumentException
  386        *              if repeatCount is < 0
  387        */
  388       public void setRepeatCount(int repeatCount) {
  389           if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) {
  390               throw new IllegalArgumentException(
  391                       "Repeat count must be >= 0, use the "
  392                               + "constant REPEAT_INDEFINITELY for infinite.");
  393           }
  394   
  395           this.repeatCount = repeatCount;
  396       }
  397   
  398       /**
  399        * <p>
  400        * Get the the time interval (in milliseconds) at which the <code>SimpleTrigger</code>
  401        * should repeat.
  402        * </p>
  403        */
  404       public long getRepeatInterval() {
  405           return repeatInterval;
  406       }
  407   
  408       /**
  409        * <p>
  410        * Set the the time interval (in milliseconds) at which the <code>SimpleTrigger</code>
  411        * should repeat.
  412        * </p>
  413        * 
  414        * @exception IllegalArgumentException
  415        *              if repeatInterval is <= 0
  416        */
  417       public void setRepeatInterval(long repeatInterval) {
  418           if (repeatInterval < 0) {
  419               throw new IllegalArgumentException(
  420                       "Repeat interval must be >= 0");
  421           }
  422   
  423           this.repeatInterval = repeatInterval;
  424       }
  425   
  426       /**
  427        * <p>
  428        * Get the number of times the <code>SimpleTrigger</code> has already
  429        * fired.
  430        * </p>
  431        */
  432       public int getTimesTriggered() {
  433           return timesTriggered;
  434       }
  435   
  436       /**
  437        * <p>
  438        * Set the number of times the <code>SimpleTrigger</code> has already
  439        * fired.
  440        * </p>
  441        */
  442       public void setTimesTriggered(int timesTriggered) {
  443           this.timesTriggered = timesTriggered;
  444       }
  445   
  446       protected boolean validateMisfireInstruction(int misfireInstruction) {
  447           if (misfireInstruction < MISFIRE_INSTRUCTION_SMART_POLICY) {
  448               return false;
  449           }
  450   
  451           if (misfireInstruction > MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
  452               return false;
  453           }
  454   
  455           return true;
  456       }
  457   
  458       /**
  459        * <p>
  460        * Updates the <code>SimpleTrigger</code>'s state based on the
  461        * MISFIRE_INSTRUCTION_XXX that was selected when the <code>SimpleTrigger</code>
  462        * was created.
  463        * </p>
  464        * 
  465        * <p>
  466        * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
  467        * then the following scheme will be used: <br>
  468        * <ul>
  469        * <li>If the Repeat Count is <code>0</code>, then the instruction will
  470        * be interpreted as <code>MISFIRE_INSTRUCTION_FIRE_NOW</code>.</li>
  471        * <li>If the Repeat Count is <code>REPEAT_INDEFINITELY</code>, then
  472        * the instruction will be interpreted as <code>MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT</code>.
  473        * <b>WARNING:</b> using MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT 
  474        * with a trigger that has a non-null end-time may cause the trigger to 
  475        * never fire again if the end-time arrived during the misfire time span. 
  476        * </li>
  477        * <li>If the Repeat Count is <code>> 0</code>, then the instruction
  478        * will be interpreted as <code>MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT</code>.
  479        * </li>
  480        * </ul>
  481        * </p>
  482        */
  483       public void updateAfterMisfire(Calendar cal) {
  484           int instr = getMisfireInstruction();
  485           if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) {
  486               if (getRepeatCount() == 0) {
  487                   instr = MISFIRE_INSTRUCTION_FIRE_NOW;
  488               } else if (getRepeatCount() == REPEAT_INDEFINITELY) {
  489                   instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;
  490               } else {
  491                   // if (getRepeatCount() > 0)
  492                   instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;
  493               }
  494           } else if (instr == MISFIRE_INSTRUCTION_FIRE_NOW && getRepeatCount() != 0) {
  495               instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT;
  496           }
  497   
  498           if (instr == MISFIRE_INSTRUCTION_FIRE_NOW) {
  499               setNextFireTime(new Date());
  500           } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) {
  501               Date newFireTime = getFireTimeAfter(new Date());
  502               while (newFireTime != null && cal != null
  503                       && !cal.isTimeIncluded(newFireTime.getTime())) {
  504                   newFireTime = getFireTimeAfter(newFireTime);
  505               }
  506               setNextFireTime(newFireTime);
  507           } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) {
  508               Date newFireTime = getFireTimeAfter(new Date());
  509               while (newFireTime != null && cal != null
  510                       && !cal.isTimeIncluded(newFireTime.getTime())) {
  511                   newFireTime = getFireTimeAfter(newFireTime);
  512               }
  513               if (newFireTime != null) {
  514                   int timesMissed = computeNumTimesFiredBetween(nextFireTime,
  515                           newFireTime);
  516                   setTimesTriggered(getTimesTriggered() + timesMissed);
  517               }
  518   
  519               setNextFireTime(newFireTime);
  520           } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) {
  521               Date newFireTime = new Date();
  522               if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
  523                   setRepeatCount(getRepeatCount() - getTimesTriggered());
  524                   setTimesTriggered(0);
  525               }
  526               
  527               if (getEndTime() != null && getEndTime().before(newFireTime)) {
  528                   setNextFireTime(null); // We are past the end time
  529               } else {
  530                   setStartTime(newFireTime);
  531                   setNextFireTime(newFireTime);
  532               } 
  533           } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) {
  534               Date newFireTime = new Date();
  535   
  536               int timesMissed = computeNumTimesFiredBetween(nextFireTime,
  537                       newFireTime);
  538   
  539               if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) {
  540                   int remainingCount = getRepeatCount()
  541                           - (getTimesTriggered() + timesMissed);
  542                   if (remainingCount <= 0) { 
  543                       remainingCount = 0;
  544                   }
  545                   setRepeatCount(remainingCount);
  546                   setTimesTriggered(0);
  547               }
  548   
  549               if (getEndTime() != null && getEndTime().before(newFireTime)) {
  550                   setNextFireTime(null); // We are past the end time
  551               } else {
  552                   setStartTime(newFireTime);
  553                   setNextFireTime(newFireTime);
  554               } 
  555           }
  556   
  557       }
  558   
  559       /**
  560        * <p>
  561        * Called when the <code>{@link Scheduler}</code> has decided to 'fire'
  562        * the trigger (execute the associated <code>Job</code>), in order to
  563        * give the <code>Trigger</code> a chance to update itself for its next
  564        * triggering (if any).
  565        * </p>
  566        * 
  567        * @see #executionComplete(JobExecutionContext, JobExecutionException)
  568        */
  569       public void triggered(Calendar calendar) {
  570           timesTriggered++;
  571           previousFireTime = nextFireTime;
  572           nextFireTime = getFireTimeAfter(nextFireTime);
  573   
  574           while (nextFireTime != null && calendar != null
  575                   && !calendar.isTimeIncluded(nextFireTime.getTime())) {
  576               nextFireTime = getFireTimeAfter(nextFireTime);
  577           }
  578       }
  579   
  580       /**
  581        *  
  582        * @see org.quartz.Trigger#updateWithNewCalendar(org.quartz.Calendar, long)
  583        */
  584       public void updateWithNewCalendar(Calendar calendar, long misfireThreshold)
  585       {
  586           nextFireTime = getFireTimeAfter(previousFireTime);
  587           
  588           Date now = new Date();
  589           do {
  590               while (nextFireTime != null && calendar != null
  591                       && !calendar.isTimeIncluded(nextFireTime.getTime())) {
  592                   nextFireTime = getFireTimeAfter(nextFireTime);
  593               }
  594               
  595               if(nextFireTime != null && nextFireTime.before(now)) {
  596                   long diff = now.getTime() - nextFireTime.getTime();
  597                   if(diff >= misfireThreshold) {
  598                       nextFireTime = getFireTimeAfter(nextFireTime);
  599                       continue;
  600                   }
  601               }
  602           }while(false);
  603       }
  604   
  605       /**
  606        * <p>
  607        * Called by the scheduler at the time a <code>Trigger</code> is first
  608        * added to the scheduler, in order to have the <code>Trigger</code>
  609        * compute its first fire time, based on any associated calendar.
  610        * </p>
  611        * 
  612        * <p>
  613        * After this method has been called, <code>getNextFireTime()</code>
  614        * should return a valid answer.
  615        * </p>
  616        * 
  617        * @return the first time at which the <code>Trigger</code> will be fired
  618        *         by the scheduler, which is also the same value <code>getNextFireTime()</code>
  619        *         will return (until after the first firing of the <code>Trigger</code>).
  620        *         </p>
  621        */
  622       public Date computeFirstFireTime(Calendar calendar) {
  623           nextFireTime = getStartTime();
  624   
  625           while (nextFireTime != null && calendar != null
  626                   && !calendar.isTimeIncluded(nextFireTime.getTime())) {
  627               nextFireTime = getFireTimeAfter(nextFireTime);
  628           }
  629   
  630           return nextFireTime;
  631       }
  632   
  633       /**
  634        * <p>
  635        * Called after the <code>{@link Scheduler}</code> has executed the
  636        * <code>{@link org.quartz.JobDetail}</code> associated with the <code>Trigger</code>
  637        * in order to get the final instruction code from the trigger.
  638        * </p>
  639        * 
  640        * @param context
  641        *          is the <code>JobExecutionContext</code> that was used by the
  642        *          <code>Job</code>'s<code>execute(xx)</code> method.
  643        * @param result
  644        *          is the <code>JobExecutionException</code> thrown by the
  645        *          <code>Job</code>, if any (may be null).
  646        * @return one of the Trigger.INSTRUCTION_XXX constants.
  647        * 
  648        * @see #INSTRUCTION_NOOP
  649        * @see #INSTRUCTION_RE_EXECUTE_JOB
  650        * @see #INSTRUCTION_DELETE_TRIGGER
  651        * @see #INSTRUCTION_SET_TRIGGER_COMPLETE
  652        * @see #triggered(Calendar)
  653        */
  654       public int executionComplete(JobExecutionContext context,
  655               JobExecutionException result) {
  656           if (result != null && result.refireImmediately()) {
  657               return INSTRUCTION_RE_EXECUTE_JOB;
  658           }
  659   
  660           if (result != null && result.unscheduleFiringTrigger()) {
  661               return INSTRUCTION_SET_TRIGGER_COMPLETE;
  662           }
  663   
  664           if (result != null && result.unscheduleAllTriggers()) {
  665               return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
  666           }
  667   
  668           if (!mayFireAgain()) {
  669               return INSTRUCTION_DELETE_TRIGGER;
  670           }
  671   
  672           return INSTRUCTION_NOOP;
  673       }
  674   
  675       /**
  676        * <p>
  677        * Returns the next time at which the <code>SimpleTrigger</code> will
  678        * fire. If the trigger will not fire again, <code>null</code> will be
  679        * returned. The value returned is not guaranteed to be valid until after
  680        * the <code>Trigger</code> has been added to the scheduler.
  681        * </p>
  682        */
  683       public Date getNextFireTime() {
  684           return nextFireTime;
  685       }
  686   
  687       /**
  688        * <p>
  689        * Returns the previous time at which the <code>SimpleTrigger</code> will
  690        * fire. If the trigger has not yet fired, <code>null</code> will be
  691        * returned.
  692        */
  693       public Date getPreviousFireTime() {
  694           return previousFireTime;
  695       }
  696   
  697       /**
  698        * <p>
  699        * Set the next time at which the <code>SimpleTrigger</code> should fire.
  700        * </p>
  701        * 
  702        * <p>
  703        * <b>This method should not be invoked by client code.</b>
  704        * </p>
  705        */
  706       public void setNextFireTime(Date nextFireTime) {
  707           this.nextFireTime = nextFireTime;
  708       }
  709   
  710       /**
  711        * <p>
  712        * Set the previous time at which the <code>SimpleTrigger</code> fired.
  713        * </p>
  714        * 
  715        * <p>
  716        * <b>This method should not be invoked by client code.</b>
  717        * </p>
  718        */
  719       public void setPreviousFireTime(Date previousFireTime) {
  720           this.previousFireTime = previousFireTime;
  721       }
  722   
  723       /**
  724        * <p>
  725        * Returns the next time at which the <code>SimpleTrigger</code> will
  726        * fire, after the given time. If the trigger will not fire after the given
  727        * time, <code>null</code> will be returned.
  728        * </p>
  729        */
  730       public Date getFireTimeAfter(Date afterTime) {
  731           if (complete) {
  732               return null;
  733           }
  734   
  735           if ((timesTriggered > repeatCount)
  736                   && (repeatCount != REPEAT_INDEFINITELY)) {
  737               return null;
  738           }
  739   
  740           if (afterTime == null) {
  741               afterTime = new Date();
  742           }
  743   
  744           if (repeatCount == 0 && afterTime.compareTo(getStartTime()) >= 0) {
  745               return null;
  746           }
  747   
  748           long startMillis = getStartTime().getTime();
  749           long afterMillis = afterTime.getTime();
  750           long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime()
  751                   .getTime();
  752   
  753           if (endMillis <= afterMillis) {
  754               return null;
  755           }
  756   
  757           if (afterMillis < startMillis) {
  758               return new Date(startMillis);
  759           }
  760   
  761           long numberOfTimesExecuted = ((afterMillis - startMillis) / repeatInterval) + 1;
  762   
  763           if ((numberOfTimesExecuted > repeatCount) && 
  764               (repeatCount != REPEAT_INDEFINITELY)) {
  765               return null;
  766           }
  767   
  768           Date time = new Date(startMillis + (numberOfTimesExecuted * repeatInterval));
  769   
  770           if (endMillis <= time.getTime()) {
  771               return null;
  772           }
  773   
  774           return time;
  775       }
  776   
  777       /**
  778        * <p>
  779        * Returns the last time at which the <code>SimpleTrigger</code> will
  780        * fire, before the given time. If the trigger will not fire before the
  781        * given time, <code>null</code> will be returned.
  782        * </p>
  783        */
  784       public Date getFireTimeBefore(Date end) {
  785           if (end.getTime() < getStartTime().getTime()) {
  786               return null;
  787           }
  788   
  789           int numFires = computeNumTimesFiredBetween(getStartTime(), end);
  790   
  791           return new Date(getStartTime().getTime() + (numFires * repeatInterval));
  792       }
  793   
  794       public int computeNumTimesFiredBetween(Date start, Date end) {
  795   
  796           if(repeatInterval < 1) {
  797               return 0;
  798           }
  799           
  800           long time = end.getTime() - start.getTime();
  801   
  802           return (int) (time / repeatInterval);
  803       }
  804   
  805       /**
  806        * <p>
  807        * Returns the final time at which the <code>SimpleTrigger</code> will
  808        * fire, if repeatCount is REPEAT_INDEFINITELY, null will be returned.
  809        * </p>
  810        * 
  811        * <p>
  812        * Note that the return time may be in the past.
  813        * </p>
  814        */
  815       public Date getFinalFireTime() {
  816           if (repeatCount == 0) {
  817               return startTime;
  818           }
  819   
  820           if (repeatCount == REPEAT_INDEFINITELY) {
  821               return (getEndTime() == null) ? null : getFireTimeBefore(getEndTime()); 
  822           }
  823   
  824           long lastTrigger = startTime.getTime() + (repeatCount * repeatInterval);
  825   
  826           if ((getEndTime() == null) || (lastTrigger < getEndTime().getTime())) { 
  827               return new Date(lastTrigger);
  828           } else {
  829               return getFireTimeBefore(getEndTime());
  830           }
  831       }
  832   
  833       /**
  834        * <p>
  835        * Determines whether or not the <code>SimpleTrigger</code> will occur
  836        * again.
  837        * </p>
  838        */
  839       public boolean mayFireAgain() {
  840           return (getNextFireTime() != null);
  841       }
  842   
  843       /**
  844        * <p>
  845        * Validates whether the properties of the <code>JobDetail</code> are
  846        * valid for submission into a <code>Scheduler</code>.
  847        * 
  848        * @throws IllegalStateException
  849        *           if a required property (such as Name, Group, Class) is not
  850        *           set.
  851        */
  852       public void validate() throws SchedulerException {
  853           super.validate();
  854   
  855           if (repeatCount != 0 && repeatInterval < 1) {
  856               throw new SchedulerException("Repeat Interval cannot be zero.",
  857                       SchedulerException.ERR_CLIENT_ERROR);
  858           }
  859       }
  860   
  861       public static void main(String[] args) // TODO: remove method after good
  862               // unit testing
  863           throws Exception {
  864   
  865           Date sdt = new Date();
  866   
  867           Date edt = new Date(sdt.getTime() + 55000L);
  868   
  869           SimpleTrigger st = new SimpleTrigger("t", "g", "j", "g", sdt, edt, 10,
  870                   10000L);
  871   
  872           System.err.println();
  873   
  874           st.computeFirstFireTime(null);
  875   
  876           System.err.println("lastTime=" + st.getFinalFireTime());
  877   
  878           java.util.List times = TriggerUtils.computeFireTimes(st, null, 50);
  879   
  880           for (int i = 0; i < times.size(); i++) {
  881               System.err.println("firetime = " + times.get(i));
  882           }
  883       }
  884   
  885   }

Home » quartz-1.6.0 » org » quartz » [javadoc | source]