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 |