View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy 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,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.log4j.scheduler;
    19
    20  import java.util.List;
    21  import java.util.Vector;
    22
    23  /**
    24   * A simple but still useful implementation of a Scheduler (in memory only).
    25   * <p></p>
    26   * This implementation will work very well when the number of scheduled job is
    27   * small, say less than 100 jobs. If a larger number of events need to be
    28   * scheduled, than a better adapted data structure for the jobList can give
    29   * improved performance.
    30   *
    31   * @author Ceki
    32   */
    33  public class Scheduler extends Thread {
    34
    35      /**
    36       * Job list.
    37       */
    38      List jobList;
    39      /**
    40       * If set true, scheduler has or should shut down.
    41       */
    42      boolean shutdown = false;
    43
    44      /**
    45       * Create new instance.
    46       */
    47      public Scheduler() {
    48          super();
    49          jobList = new Vector();
    50      }
    51
    52      /**
    53       * Find the index of a given job.
    54       * @param job job
    55       * @return -1 if the job could not be found.
    56       */
    57      int findIndex(final Job job) {
    58          int size = jobList.size();
    59          boolean found = false;
    60
    61          int i = 0;
    62          for (; i < size; i++) {
    63              ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i);
    64              if (se.job == job) {
    65                  found = true;
    66                  break;
    67              }
    68          }
    69          if (found) {
    70              return i;
    71          } else {
    72              return -1;
    73          }
    74      }
    75
    76      /**
    77       * Delete the given job.
    78       * @param job job.
    79       * @return true if the job could be deleted, and
    80       * false if the job could not be found or if the Scheduler is about to
    81       * shutdown in which case deletions are not permitted.
    82       */
    83      public synchronized boolean delete(final Job job) {
    84          // if already shutdown in the process of shutdown, there is no
    85          // need to remove Jobs as they will never be executed.
    86          if (shutdown) {
    87              return false;
    88          }
    89          int i = findIndex(job);
    90          if (i != -1) {
    91              ScheduledJobEntry se = (ScheduledJobEntry) jobList.remove(i);
    92              if (se.job != job) { // this should never happen
    93                  new IllegalStateException("Internal programming error");
    94              }
    95              // if the job is the first on the list,
    96              // then notify the scheduler thread to schedule a new job
    97              if (i == 0) {
    98                  this.notifyAll();
    99              }
    100             return true;
    101         } else {
    102             return false;
    103         }
    104     }
    105
    106
    107     /**
    108      * Schedule a {@link Job} for execution at system time given by
    109      * the <code>desiredTime</code> parameter.
    110      * @param job job to schedule.
    111      * @param desiredTime desired time of execution.
    112      */
    113     public synchronized void schedule(final Job job,
    114                                       final long desiredTime) {
    115         schedule(new ScheduledJobEntry(job, desiredTime));
    116     }
    117
    118     /**
    119      * Schedule a {@link Job} for execution at system time given by
    120      * the <code>desiredTime</code> parameter.
    121      * <p></p>
    122      * The job will be rescheduled. It will execute with a frequency determined
    123      * by the period parameter.
    124      * @param job job to schedule.
    125      * @param desiredTime desired time of execution.
    126      * @param period repeat period.
    127      */
    128     public synchronized void schedule(final Job job,
    129                                       final long desiredTime,
    130                                       final long period) {
    131         schedule(new ScheduledJobEntry(job, desiredTime, period));
    132     }
    133
    134     /**
    135      * Change the period of a job. The original job must exist for its period
    136      * to be changed.
    137      * <p></p>
    138      * The method returns true if the period could be changed, and false
    139      * otherwise.
    140      * @param job job.
    141      * @param newPeriod new repeat period.
    142      * @return true if period could be changed.
    143      */
    144     public synchronized boolean changePeriod(final Job job,
    145                                              final long newPeriod) {
    146         if (newPeriod <= 0) {
    147             throw new IllegalArgumentException(
    148                     "Period must be an integer langer than zero");
    149         }
    150
    151         int i = findIndex(job);
    152         if (i == -1) {
    153             return false;
    154         } else {
    155             ScheduledJobEntry se = (ScheduledJobEntry) jobList.get(i);
    156             se.period = newPeriod;
    157             return true;
    158         }
    159     }
    160
    161     /**
    162      * Schedule a job.
    163      * @param newSJE new job entry.
    164      */
    165     private synchronized void schedule(final ScheduledJobEntry newSJE) {
    166         // disallow new jobs after shutdown
    167         if (shutdown) {
    168             return;
    169         }
    170         int max = jobList.size();
    171         long desiredExecutionTime = newSJE.desiredExecutionTime;
    172
    173         // find the index i such that timeInMillis < jobList[i]
    174         int i = 0;
    175         for (; i < max; i++) {
    176
    177             ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(i);
    178
    179             if (desiredExecutionTime < sje.desiredExecutionTime) {
    180                 break;
    181             }
    182         }
    183         jobList.add(i, newSJE);
    184         // if the jobList was empty, then notify the scheduler thread
    185         if (i == 0) {
    186             this.notifyAll();
    187         }
    188     }
    189
    190     /**
    191      * Shut down scheduler.
    192      */
    193     public synchronized void shutdown() {
    194         shutdown = true;
    195     }
    196
    197     /**
    198      * Run scheduler.
    199      */
    200     public synchronized void run() {
    201         while (!shutdown) {
    202             if (jobList.isEmpty()) {
    203                 linger();
    204             } else {
    205                 ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(0);
    206                 long now = System.currentTimeMillis();
    207                 if (now >= sje.desiredExecutionTime) {
    208                     executeInABox(sje.job);
    209                     jobList.remove(0);
    210                     if (sje.period > 0) {
    211                         sje.desiredExecutionTime = now + sje.period;
    212                         schedule(sje);
    213                     }
    214                 } else {
    215                     linger(sje.desiredExecutionTime - now);
    216                 }
    217             }
    218         }
    219         // clear out the job list to facilitate garbage collection
    220         jobList.clear();
    221         jobList = null;
    222         System.out.println("Leaving scheduler run method");
    223     }
    224
    225     /**
    226      * We do not want a single failure to affect the whole scheduler.
    227      * @param job job to execute.
    228      */
    229     void executeInABox(final Job job) {
    230         try {
    231             job.execute();
    232         } catch (Exception e) {
    233             System.err.println("The execution of the job threw an exception");
    234             e.printStackTrace(System.err);
    235         }
    236     }
    237
    238     /**
    239      * Wait for notification.
    240      */
    241     void linger() {
    242         try {
    243             while (jobList.isEmpty() && !shutdown) {
    244                 this.wait();
    245             }
    246         } catch (InterruptedException ie) {
    247             shutdown = true;
    248         }
    249     }
    250
    251     /**
    252      * Wait for notification or time to elapse.
    253      * @param timeToLinger time to linger.
    254      */
    255     void linger(final long timeToLinger) {
    256         try {
    257             this.wait(timeToLinger);
    258         } catch (InterruptedException ie) {
    259             shutdown = true;
    260         }
    261     }
    262
    263     /**
    264      * Represents an entry in job scheduler.
    265      */
    266     static final class ScheduledJobEntry {
    267         /**
    268          * Desired execution time.
    269          */
    270         long desiredExecutionTime;
    271         /**
    272          * Job to run.
    273          */
    274         Job job;
    275         /**
    276          * Repeat period.
    277          */
    278         long period = 0;
    279
    280         /**
    281          * Create new instance.
    282          * @param job job
    283          * @param desiredTime desired time.
    284          */
    285         ScheduledJobEntry(final Job job, final long desiredTime) {
    286             this(job, desiredTime, 0);
    287         }
    288
    289         /**
    290          * Create new instance.
    291          * @param job job
    292          * @param desiredTime desired time
    293          * @param period repeat period
    294          */
    295         ScheduledJobEntry(final Job job,
    296                           final long desiredTime,
    297                           final long period) {
    298             super();
    299             this.desiredExecutionTime = desiredTime;
    300             this.job = job;
    301             this.period = period;
    302         }
    303     }
    304
    305 }
    306
    307