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<ScheduledJobEntry> 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       *
    55       * @param job job
    56       * @return -1 if the job could not be found.
    57       */
    58      int findIndex(final Job job) {
    59          int size = jobList.size();
    60          boolean found = false;
    61
    62          int i = 0;
    63          for (; i < size; i++) {
    64              ScheduledJobEntry se = jobList.get(i);
    65              if (se.job == job) {
    66                  found = true;
    67                  break;
    68              }
    69          }
    70          if (found) {
    71              return i;
    72          } else {
    73              return -1;
    74          }
    75      }
    76
    77      /**
    78       * Delete the given job.
    79       *
    80       * @param job job.
    81       * @return true if the job could be deleted, and
    82       * false if the job could not be found or if the Scheduler is about to
    83       * shutdown in which case deletions are not permitted.
    84       */
    85      public synchronized boolean delete(final Job job) {
    86          // if already shutdown in the process of shutdown, there is no
    87          // need to remove Jobs as they will never be executed.
    88          if (shutdown) {
    89              return false;
    90          }
    91          int i = findIndex(job);
    92          if (i != -1) {
    93              ScheduledJobEntry se = jobList.remove(i);
    94              if (se.job != job) { // this should never happen
    95                  new IllegalStateException("Internal programming error");
    96              }
    97              // if the job is the first on the list,
    98              // then notify the scheduler thread to schedule a new job
    99              if (i == 0) {
    100                 this.notifyAll();
    101             }
    102             return true;
    103         } else {
    104             return false;
    105         }
    106     }
    107
    108
    109     /**
    110      * Schedule a {@link Job} for execution at system time given by
    111      * the <code>desiredTime</code> parameter.
    112      *
    113      * @param job         job to schedule.
    114      * @param desiredTime desired time of execution.
    115      */
    116     public synchronized void schedule(final Job job,
    117                                       final long desiredTime) {
    118         schedule(new ScheduledJobEntry(job, desiredTime));
    119     }
    120
    121     /**
    122      * Schedule a {@link Job} for execution at system time given by
    123      * the <code>desiredTime</code> parameter.
    124      * <p></p>
    125      * The job will be rescheduled. It will execute with a frequency determined
    126      * by the period parameter.
    127      *
    128      * @param job         job to schedule.
    129      * @param desiredTime desired time of execution.
    130      * @param period      repeat period.
    131      */
    132     public synchronized void schedule(final Job job,
    133                                       final long desiredTime,
    134                                       final long period) {
    135         schedule(new ScheduledJobEntry(job, desiredTime, period));
    136     }
    137
    138     /**
    139      * Change the period of a job. The original job must exist for its period
    140      * to be changed.
    141      * <p></p>
    142      * The method returns true if the period could be changed, and false
    143      * otherwise.
    144      *
    145      * @param job       job.
    146      * @param newPeriod new repeat period.
    147      * @return true if period could be changed.
    148      */
    149     public synchronized boolean changePeriod(final Job job,
    150                                              final long newPeriod) {
    151         if (newPeriod <= 0) {
    152             throw new IllegalArgumentException(
    153                 "Period must be an integer langer than zero");
    154         }
    155
    156         int i = findIndex(job);
    157         if (i == -1) {
    158             return false;
    159         } else {
    160             ScheduledJobEntry se = jobList.get(i);
    161             se.period = newPeriod;
    162             return true;
    163         }
    164     }
    165
    166     /**
    167      * Schedule a job.
    168      *
    169      * @param newSJE new job entry.
    170      */
    171     private synchronized void schedule(final ScheduledJobEntry newSJE) {
    172         // disallow new jobs after shutdown
    173         if (shutdown) {
    174             return;
    175         }
    176         int max = jobList.size();
    177         long desiredExecutionTime = newSJE.desiredExecutionTime;
    178
    179         // find the index i such that timeInMillis < jobList[i]
    180         int i = 0;
    181         for (; i < max; i++) {
    182
    183             ScheduledJobEntry sje = jobList.get(i);
    184
    185             if (desiredExecutionTime < sje.desiredExecutionTime) {
    186                 break;
    187             }
    188         }
    189         jobList.add(i, newSJE);
    190         // if the jobList was empty, then notify the scheduler thread
    191         if (i == 0) {
    192             this.notifyAll();
    193         }
    194     }
    195
    196     /**
    197      * Shut down scheduler.
    198      */
    199     public synchronized void shutdown() {
    200         shutdown = true;
    201     }
    202
    203     /**
    204      * Run scheduler.
    205      */
    206     public synchronized void run() {
    207         while (!shutdown) {
    208             if (jobList.isEmpty()) {
    209                 linger();
    210             } else {
    211                 ScheduledJobEntry sje = jobList.get(0);
    212                 long now = System.currentTimeMillis();
    213                 if (now >= sje.desiredExecutionTime) {
    214                     executeInABox(sje.job);
    215                     jobList.remove(0);
    216                     if (sje.period > 0) {
    217                         sje.desiredExecutionTime = now + sje.period;
    218                         schedule(sje);
    219                     }
    220                 } else {
    221                     linger(sje.desiredExecutionTime - now);
    222                 }
    223             }
    224         }
    225         // clear out the job list to facilitate garbage collection
    226         jobList.clear();
    227         jobList = null;
    228         System.out.println("Leaving scheduler run method");
    229     }
    230
    231     /**
    232      * We do not want a single failure to affect the whole scheduler.
    233      *
    234      * @param job job to execute.
    235      */
    236     void executeInABox(final Job job) {
    237         try {
    238             job.execute();
    239         } catch (Exception e) {
    240             System.err.println("The execution of the job threw an exception");
    241             e.printStackTrace(System.err);
    242         }
    243     }
    244
    245     /**
    246      * Wait for notification.
    247      */
    248     void linger() {
    249         try {
    250             while (jobList.isEmpty() && !shutdown) {
    251                 this.wait();
    252             }
    253         } catch (InterruptedException ie) {
    254             shutdown = true;
    255         }
    256     }
    257
    258     /**
    259      * Wait for notification or time to elapse.
    260      *
    261      * @param timeToLinger time to linger.
    262      */
    263     void linger(final long timeToLinger) {
    264         try {
    265             this.wait(timeToLinger);
    266         } catch (InterruptedException ie) {
    267             shutdown = true;
    268         }
    269     }
    270
    271     /**
    272      * Represents an entry in job scheduler.
    273      */
    274     static final class ScheduledJobEntry {
    275         /**
    276          * Desired execution time.
    277          */
    278         long desiredExecutionTime;
    279         /**
    280          * Job to run.
    281          */
    282         Job job;
    283         /**
    284          * Repeat period.
    285          */
    286         long period = 0;
    287
    288         /**
    289          * Create new instance.
    290          *
    291          * @param job         job
    292          * @param desiredTime desired time.
    293          */
    294         ScheduledJobEntry(final Job job, final long desiredTime) {
    295             this(job, desiredTime, 0);
    296         }
    297
    298         /**
    299          * Create new instance.
    300          *
    301          * @param job         job
    302          * @param desiredTime desired time
    303          * @param period      repeat period
    304          */
    305         ScheduledJobEntry(final Job job,
    306                           final long desiredTime,
    307                           final long period) {
    308             super();
    309             this.desiredExecutionTime = desiredTime;
    310             this.job = job;
    311             this.period = period;
    312         }
    313     }
    314
    315 }
    316
    317