File Source: TaskScheduler.java

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   *  contributor license agreements.  The ASF licenses this file to You
     4   * under the Apache License, Version 2.0 (the "License"); you may not
     5   * use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.  For additional information regarding
    15   * copyright in this work, please see the NOTICE file in the top level
    16   * directory of this distribution.
    17   */
    18  
    19  package org.apache.roller.weblogger.business.runnable;
    20  
    21  import java.util.Calendar;
    22  import java.util.Date;
    23  import java.util.List;
    24  import java.util.concurrent.ExecutorService;
    25  import java.util.concurrent.Executors;
    26  import org.apache.commons.logging.Log;
    27  import org.apache.commons.logging.LogFactory;
    28  import org.apache.roller.util.DateUtil;
    29  import org.apache.roller.weblogger.business.WebloggerFactory;
    30  import org.apache.roller.weblogger.pojos.TaskLock;
    31  
    32  
    33  /**
    34   * Manages scheduling of periodic tasks.
    35   * 
    36   * This scheduler is meant to be run on a single thread and once started it will
    37   * run continuously until the thread is interrupted.  The basic logic of the
    38   * scheduler is to accept some number of tasks to be run and once per minute
    39   * the scheduler will launch any tasks that need to be executed.  
    40   * 
    41   * Tasks are executed each on their own thread, so this scheduler does not run
    42   * serially like a TimerTask.  The threads used for running tasks are managed
    43   * by an instance of a ThreadPoolExecutor.
    44   */
    45  public class TaskScheduler implements Runnable {
    46      
             /* 
    P/P       *  Method: org.apache.roller.weblogger.business.runnable.TaskScheduler__static_init
              * 
              *  Postconditions:
              *    init'ed(log)
              */
    47      private static Log log = LogFactory.getLog(TaskScheduler.class);
    48      
    49      private static final long ONE_MINUTE_MS = (60 * 1000);
    50      
    51      private final ExecutorService pool;
    52      private final List<RollerTask> tasks;
    53      
    54      
             /* 
    P/P       *  Method: void org.apache.roller.weblogger.business.runnable.TaskScheduler(List)
              * 
              *  Postconditions:
              *    init'ed(this.pool)
              *    this.tasks == webloggerTasks
              *    init'ed(this.tasks)
              */
    55      public TaskScheduler(List<RollerTask> webloggerTasks) {
    56          
    57          // store list of tasks available to run
    58          tasks = webloggerTasks;
    59          
    60          // use an expanding thread executor pool
    61          pool = Executors.newCachedThreadPool();
    62      }
    63      
    64      
    65      public void run() {
    66          
                 /* 
    P/P           *  Method: void run()
                  * 
                  *  Preconditions:
                  *    this.pool != null
                  *    (soft) log != null
                  *    (soft) org/apache/roller/weblogger/business/WebloggerFactory.webloggerProvider != null
                  *    (soft) org/apache/roller/weblogger/business/WebloggerFactory.webloggerProvider.webloggerInstance != null
                  *    (soft) org/apache/roller/weblogger/business/WebloggerImpl.log != null
                  *    (soft) this.tasks != null
                  * 
                  *  Presumptions:
                  *    getWeblogger(...).autoPingManager != null
                  *    getWeblogger(...).bookmarkManager != null
                  *    getWeblogger(...).fileManager != null
                  *    getWeblogger(...).pingQueueManager != null
                  *    getWeblogger(...).pingTargetManager != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    java.lang.System:currentTimeMillis(...)@102 - java.util.Date:getTime(...)@102: {50..263+50}, {-18_446_744_073_709_551_565..49}
                  */
    67          boolean firstRun = true;
    68          
    69          // run forever, or until we get interrupted
    70          while(true) {
    71              try {
    72                  // get current time
    73                  Date now = new Date();
    74                  log.debug("Current time = "+now);
    75                  
    76                  // run tasks, skip run on first pass
    77                  if(firstRun) {
    78                      // add a slight delay to scheduler start
    79                      Calendar cal = Calendar.getInstance();
    80                      cal.setTime(now);
    81                      cal.add(Calendar.MINUTE, 1);
    82                      cal.set(Calendar.SECOND, cal.getMinimum(Calendar.SECOND));
    83                      cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND));
    84                      now = cal.getTime();
    85                      log.debug("Start time = "+now);
    86                      
    87                      firstRun = false;
    88                  } else {
    89                      try {
    90                          runTasks(now);
    91                      } finally {
    92                          // always release session after each pass
    93                          WebloggerFactory.getWeblogger().release();
    94                      }
    95                  }
    96                  
    97                  // wait 'til next minute
    98                  // NOTE: we add 50ms of adjustment time to make sure we awaken
    99                  //       during the next minute, and not before.  awakening at
   100                  //       exactly the .000ms is not of any concern to us
   101                  Date endOfMinute = DateUtil.getEndOfMinute(now);
   102                  long sleepTime = (endOfMinute.getTime() + 50) - System.currentTimeMillis();
   103                  if(sleepTime > 0) {
   104                      log.debug("sleeping - "+sleepTime);
   105                      Thread.sleep(sleepTime);
   106                  } else {
   107                      // it's taken us more than 1 minute for the last loop
   108                      // so recalculate to sleep 'til the end of the current minute
   109                      endOfMinute = DateUtil.getEndOfMinute(new Date());
   110                      sleepTime = (endOfMinute.getTime() + 50) - System.currentTimeMillis();
   111                      log.debug("sleeping - "+sleepTime);
   112                      Thread.sleep(sleepTime);
   113                  }
   114                  
   115              } catch (InterruptedException ex) {
   116                  // thread interrupted
   117                  log.debug("Thread interrupted, scheduler is stopping");
   118                  pool.shutdownNow();
   119                  break;
   120              }
   121          }
   122          
   123      }
   124      
   125      
   126      /**
   127       * Run the necessary tasks given a specific currentTime to work from.
   128       */
   129      private void runTasks(Date currentTime) {
   130          
                 /* 
    P/P           *  Method: void runTasks(Date)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    org/apache/roller/weblogger/business/WebloggerFactory.webloggerProvider != null
                  *    org/apache/roller/weblogger/business/WebloggerFactory.webloggerProvider.webloggerInstance != null
                  *    this.tasks != null
                  *    (soft) currentTime != null
                  *    (soft) this.pool != null
                  * 
                  *  Presumptions:
                  *    getWeblogger(...).threadManager != null
                  *    java.util.Date:getTime(...)@180 - java.util.Date:getTime(...)@180 in -264+1..263
                  *    java.util.Iterator:next(...)@135 != null
                  *    org.apache.roller.util.DateUtil:getStartOfDay(...)@160 != null
                  *    org.apache.roller.util.DateUtil:getStartOfHour(...)@168 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@157: {0}, {1}
                  *    java.lang.String:equals(...)@165: {0}, {1}
                  *    java.util.Date:getTime(...)@180 - java.util.Date:getTime(...)@180: {1..263}, {-264+1..0}
                  *    java.util.Iterator:hasNext(...)@135: {0}, {1}
                  */
   131          log.debug("Started - "+currentTime);
   132          
   133          ThreadManager tmgr = WebloggerFactory.getWeblogger().getThreadManager();
   134          
   135          for( RollerTask task : tasks ) {
   136              try {
   137                  // get tasklock for the task
   138                  TaskLock tasklock = tmgr.getTaskLockByName(task.getName());
   139                  
   140                  // TODO: check if task is enabled, otherwise skip
   141                  if(tasklock == null) {
+  142                      log.debug("SKIPPING task : "+tasklock.getName());
   143                      continue;
   144                  }
   145                  
   146                  // first, calculate the next allowed run time for the task
   147                  // based on when the task was last run
   148                  Date nextRunTime = tasklock.getNextAllowedRun(task.getInterval());
   149                  log.debug(task.getName()+": next allowed run time = "+nextRunTime);
   150                  
   151                  // if we missed the last scheduled run time then see when the
   152                  // most appropriate next run time should be and wait 'til then
   153                  boolean needToWait = false;
   154                  if(currentTime.getTime() > (nextRunTime.getTime() + ONE_MINUTE_MS)) {
   155                      
   156                      log.debug("MISSED last run, checking if waiting is necessary");
   157                      if("startOfDay".equals(task.getStartTimeDesc())) {
   158                          // for daily tasks we only run during the first 
   159                          // couple minutes of the day
   160                          Date startOfDay = DateUtil.getStartOfDay(currentTime);
   161                          if(currentTime.getTime() > startOfDay.getTime() + (2 * ONE_MINUTE_MS)) {
   162                              needToWait = true;
   163                              log.debug("WAITING for next reasonable run time");
   164                          }
   165                      } else if("startOfHour".equals(task.getStartTimeDesc())) {
   166                          // for hourly tasks we only run during the first
   167                          // couple minutes of the hour
   168                          Date startOfHour = DateUtil.getStartOfHour(currentTime);
   169                          if(currentTime.getTime() > startOfHour.getTime() + (2 * ONE_MINUTE_MS)) {
   170                              needToWait = true;
   171                              log.debug("WAITING for next reasonable run time");
   172                          }
   173                      } else {
   174                          // for immediate tasks we just go ahead and run
   175                      }
   176                  }
   177                  
   178                  // if we are within 1 minute of run time then execute,
   179                  // otherwise we do nothing
   180                  long differential = currentTime.getTime() - nextRunTime.getTime();
   181                  if (differential >= 0 && !needToWait) {
   182                      log.debug(task.getName()+": LAUNCHING task");
   183                      pool.submit(task);
   184                  }
   185                  
   186              } catch(Throwable t) {
   187                  log.warn(task.getName()+": Unhandled exception caught", t);
   188              }
   189          }
   190          
   191          log.debug("Finished");
   192      }
   193      
   194  }








SofCheck Inspector Build Version : 2.18479
TaskScheduler.java 2009-Jan-02 14:25:28
TaskScheduler.class 2009-Sep-04 03:12:31