1 /***
2 * @(#)Task.java
3 *
4 * JFoxSOAF, Service-Oriented Application Framework
5 *
6 * Copyright(c) JFoxSOAF Team
7 *
8 * Licensed under the GNU LGPL, Version 2.1 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.gnu.org/copyleft/lesser.html
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 * For more information, please visit:
21 * http://www.jfox.cn/confluence/display/JFoxSOAF/Home
22 * http://www.huihoo.org/jfox/jfoxsoaf
23 */
24
25 package org.huihoo.jfox.soaf.services.timer;
26
27 import java.io.Externalizable;
28 import java.io.ObjectOutput;
29 import java.io.ObjectInput;
30 import java.io.IOException;
31
32 /***
33 * <p>
34 * A task which can be scheduled to run at specified times by the TaskScheduler
35 * <br>
36 * </p>
37 * <p>
38 * The user should extend this class and override the required methods (e.g.
39 * run() method).
40 * </p>
41 *
42 * @author <a href="mailto:founder_chen@yahoo.com.cn">Peter Cheng </a>
43 * @author <a href="mailto:berkovichnyc@hotmail.com">Efraim Berkovich </a>
44 * @version $Revision: 1.1 $ $Date: 2005/05/22 06:52:07 $
45 * @version Revision: 1.0
46 */
47
48 public abstract class Task implements Runnable, Externalizable {
49
50
51 /*** has the cancel() method been called? */
52 protected boolean isCancelled = false;
53
54 /*** The name of the task (for display purposes) */
55 protected String name = null;
56
57 private transient long ID;
58
59 private boolean hasBeenScheduled = false;
60
61 private long scheduledInitialTime = 0;
62
63 private long scheduledInterval = 0;
64
65 private long lastRunTime = 0;
66
67
68
69 SimpleTimerServiceImpl scheduler = null;
70
71
72 private static Boolean mutex = new Boolean(true);
73
74 private static long nextID = 0;
75
76 /***
77 * Creates a new task and assigns it a unique ID.
78 */
79 protected Task() {
80 synchronized (mutex) {
81 this.ID = nextID;
82 nextID++;
83 if (nextID == Long.MAX_VALUE)
84 nextID = 0;
85 }
86
87 }
88
89 /***
90 * Schedules the specified task for execution at the specified time. If the
91 * time is in the past, the task is scheduled for immediate execution.
92 * <p>
93 * There are two types of scheduling: one time and periodic.
94 * <p>
95 * <b>One time </b> <br>
96 * The task will execute exactly once.
97 * <p>
98 * <b>Periodic </b> <br>
99 * The task is scheduled for repeated periodic execution, beginning at the
100 * specified time. Once a task starts running, the next occurrence of the
101 * task will start running <i>interval </i> time later. It is possible for
102 * one event-firing of the task (if it runs too long) will overlap another
103 * event-firing of that same task. If this is not desired, then the task
104 * should synchronize within its run() method.
105 * <p>
106 * This method can only be called once. Calling it more than once will cause
107 * an exception.
108 * <p>
109 *
110 * @param firstTime long system time at which to run the task.
111 * @param interval long number of millis between executions of the task,
112 * passing <=0 will cause the task to be executed once.
113 * @exception IllegalStateException On calling this method more than once.
114 */
115 public synchronized final void setSchedule(long firstTime, long interval)
116 throws IllegalStateException {
117 if (hasBeenScheduled)
118 throw new IllegalStateException(
119 "Cannot set schedule for the task more than once.");
120
121 hasBeenScheduled = true;
122 this.scheduledInitialTime = firstTime;
123 this.scheduledInterval = interval;
124 }
125
126 /***
127 * Get the task ID.
128 *
129 * @return unique Task ID.
130 */
131 public long getID() {
132 return ID;
133 }
134
135 /***
136 * Get the first scheduled time for the task.
137 *
138 * @return the time the task was first scheduled to run (as long, millis)
139 */
140 public long getScheduledFirstTime() {
141 return scheduledInitialTime;
142 }
143
144 /***
145 * Get the interval scheduled for the task.
146 *
147 * @return the interval scheduled for the task. (as long, millis)
148 */
149 public long getScheduledInterval() {
150 return scheduledInterval;
151 }
152
153 /***
154 * Was the cancel method called for this task?
155 *
156 * @return true if cancel was called, false if not
157 */
158 public boolean isCancelled() {
159 return isCancelled;
160 }
161
162 /***
163 * Get the name for the task
164 *
165 * @return the task name
166 */
167 public String getName() {
168 if (name == null)
169 return this.getClass().toString();
170
171 return name;
172 }
173
174 /***
175 * Calling this method notifies the TaskScheduler (if the task has been
176 * scheduled) and forces it to persist this task's data.
177 * <p>
178 *
179 * @exception various possible depending on persist strategy of the
180 * scheduler
181 */
182 public void persist() throws Exception {
183 if (scheduler != null)
184 scheduler.persist(this);
185 }
186
187 /***
188 * Cancels this timer task. If the task has been scheduled for one-time
189 * execution and has not yet run, or has not yet been scheduled, it will
190 * never run. If the task has been scheduled for repeated execution, it will
191 * never run again. (If the task is running when this call occurs, the task
192 * will run to completion, but will never run again.)
193 * <p>
194 * Note that calling this method from within the run method of a repeating
195 * timer task absolutely guarantees that the timer task will not run again.
196 * <p>
197 * This method may be called repeatedly; the second and subsequent calls
198 * have no effect.
199 */
200 public void cancel() {
201 isCancelled = true;
202 if (scheduler != null) {
203 scheduler.cleanup(this);
204 }
205 }
206
207 /***
208 * Returns the scheduled execution time of the most recent actual execution
209 * of this task. (If this method is invoked while task execution is in
210 * progress, the return value is the scheduled execution time of the ongoing
211 * task execution.)
212 * <p>
213 * This method is typically invoked from within a task's run method, to
214 * determine whether the current execution of the task is sufficiently
215 * timely to warrant performing the scheduled activity:
216 * <p>
217 *
218 * <pre>
219 * public void run() {
220 * if (System.currentTimeMillis() - lastExecutionTime() >= MAX_TARDINESS)
221 * return; // Too late; skip this execution.
222 * // Perform the task
223 * }
224 * </pre>
225 *
226 * <p>
227 *
228 * @return the time at which the most recent execution of this task was
229 * scheduled to occur, in the format returned by Date.getTime(). The
230 * return value is undefined if the task has yet to commence its
231 * first execution.
232 */
233 public long lastExecutionTime() {
234 return lastRunTime;
235 }
236
237 /***
238 * Set the last run time. This is only called by the TaskScheduler
239 *
240 * @param lastRunTime. The last run time
241 */
242 void setLastRunTime(long lastRunTime) {
243 this.lastRunTime = lastRunTime;
244 }
245
246 /***
247 * This method persists the task. Tasks should override this method if they
248 * carry instance data. (Make sure to call super.writeExternal() if you
249 * override).
250 *
251 * @param ObjectOutput The stream to write the object to
252 * @exception IOException On write problems
253 */
254 public void writeExternal(ObjectOutput out) throws IOException {
255 out.writeLong(this.scheduledInitialTime);
256 out.writeLong(this.scheduledInterval);
257 out.writeLong(this.lastRunTime);
258 out.writeObject(this.name);
259 }
260
261 /***
262 * This method instantiates the task from persistent storage. Tasks should
263 * override this method if they carry instance data. (Make sure to call
264 * super.writeExternal() if you override).
265 * <p>
266 * When a Task is instantiated from deserialization, the
267 * scheduledInitialTime is set according to the following rules:
268 * <ul>
269 * <li>If the scheduledInitialTime is in the future, that time is kept.
270 * <li>If the scheduledInitialTime is in the past and the Task is a
271 * repeating task, then the scheduledInitialTime is set to the next time the
272 * task would run had the Task not been serialized. For example, if a Task
273 * runs once a day at 1pm, then the scheduledInitialTime will be set to the
274 * next 1pm running time. (The logic does not distinguish between fixed-rate
275 * vs. fixed-delay Tasks).
276 * </ul>
277 *
278 * @param ObjectInput The stream to write the object to
279 * @exception IOException On write problems
280 * @exception ClassNotFoundException On the class not being in the stream.
281 */
282 public void readExternal(ObjectInput in) throws IOException,
283 java.lang.ClassNotFoundException {
284 this.scheduledInitialTime = in.readLong();
285 this.scheduledInterval = in.readLong();
286 this.lastRunTime = in.readLong();
287 this.name = (String) in.readObject();
288
289
290 if (scheduledInitialTime < System.currentTimeMillis()) {
291 long n = System.currentTimeMillis() - scheduledInitialTime;
292 n = n % scheduledInterval;
293 this.scheduledInitialTime = System.currentTimeMillis() + n;
294 }
295
296
297 hasBeenScheduled = true;
298 }
299
300 /***
301 * This method is the action to be performed. This method should be
302 * overriden. It will be called once at every time the task runs.
303 * <p>
304 * If a task encounters an error during run, it may call the cancel() method
305 * to prevent the task from running again.
306 * <p>
307 * If the task modifies its instance data during execution, then it should
308 * call the Task persist() method. This will notify the TaskScheduler to
309 * write the task to persistent storage by calling the writeExternal()
310 * method.
311 */
312 public abstract void run();
313
314 /***
315 * This method should provide a status message regarding the task. Care
316 * should be taken to make sure the method is thread-safe, as code in the
317 * run() method and the caller of getStatus() will likely be in different
318 * threads.
319 *
320 * @return a task-specific status message
321 */
322 public abstract String getStatus();
323
324 }