View Javadoc

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      // instance variables
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; // if <=0 -- means one-time execution
64  
65      private long lastRunTime = 0;
66  
67      // package view -- the task scheduler sets this when the task is scheduled
68      // with it
69      SimpleTimerServiceImpl scheduler = null;
70  
71      // static variables
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() &gt;= 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         // set scheduledInitialTime to make it in the future
290         if (scheduledInitialTime < System.currentTimeMillis()) {
291             long n = System.currentTimeMillis() - scheduledInitialTime;
292             n = n % scheduledInterval;
293             this.scheduledInitialTime = System.currentTimeMillis() + n;
294         }
295 
296         // assume all deserialized tasks had setSchedule() called
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 }