[FEATURE] Grouping for scheduler-jobs
[Packages/TYPO3.CMS.git] / typo3 / sysext / scheduler / Classes / Task / AbstractTask.php
1 <?php
2 namespace TYPO3\CMS\Scheduler\Task;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2005-2013 Christian Jul Jensen (julle@typo3.org)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /**
27 * This is the base class for all Scheduler tasks
28 * It's an abstract class, not designed to be instantiated directly
29 * All Scheduler tasks should inherit from this class
30 *
31 * @author Fran├žois Suter <francois@typo3.org>
32 * @author Christian Jul Jensen <julle@typo3.org>
33 */
34 abstract class AbstractTask {
35
36 /**
37 * Reference to a scheduler object
38 *
39 * @var \TYPO3\CMS\Scheduler\Scheduler
40 */
41 protected $scheduler;
42
43 /**
44 * The unique id of the task used to identify it in the database
45 *
46 * @var integer
47 */
48 protected $taskUid;
49
50 /**
51 * Disable flag, TRUE if task is disabled, FALSE otherwise
52 *
53 * @var boolean
54 */
55 protected $disabled = FALSE;
56
57 /**
58 * The execution object related to the task
59 *
60 * @var \TYPO3\CMS\Scheduler\Execution
61 */
62 protected $execution;
63
64 /**
65 * This variable contains the time of next execution of the task
66 *
67 * @var integer
68 */
69 protected $executionTime = 0;
70
71 /**
72 * Description for the task
73 *
74 * @var string
75 */
76 protected $description = '';
77
78 /**
79 * Task group for this task
80 *
81 * @var integer
82 */
83 protected $taskGroup;
84
85 /**
86 * Constructor
87 */
88 public function __construct() {
89 $this->setScheduler();
90 $this->execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Execution');
91 }
92
93 /**
94 * This is the main method that is called when a task is executed
95 * It MUST be implemented by all classes inheriting from this one
96 * Note that there is no error handling, errors and failures are expected
97 * to be handled and logged by the client implementations.
98 * Should return TRUE on successful execution, FALSE on error.
99 *
100 * @return boolean Returns TRUE on successful execution, FALSE on error
101 */
102 abstract public function execute();
103
104 /**
105 * This method is designed to return some additional information about the task,
106 * that may help to set it apart from other tasks from the same class
107 * This additional information is used - for example - in the Scheduler's BE module
108 * This method should be implemented in most task classes
109 *
110 * @return string Information to display
111 */
112 public function getAdditionalInformation() {
113 return '';
114 }
115
116 /**
117 * This method is used to set the unique id of the task
118 *
119 * @param integer $id Primary key (from the database record) of the scheduled task
120 * @return void
121 */
122 public function setTaskUid($id) {
123 $this->taskUid = intval($id);
124 }
125
126 /**
127 * This method returns the unique id of the task
128 *
129 * @return integer The id of the task
130 */
131 public function getTaskUid() {
132 return $this->taskUid;
133 }
134
135 /**
136 * This method returns the title of the scheduler task
137 *
138 * @return string
139 */
140 public function getTaskTitle() {
141 return $GLOBALS['LANG']->sL($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][get_class($this)]['title']);
142 }
143
144 /**
145 * This method returns the description of the scheduler task
146 *
147 * @return string
148 */
149 public function getTaskDescription() {
150 return $GLOBALS['LANG']->sL($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][get_class($this)]['description']);
151 }
152
153 /**
154 * This method returns the class name of the scheduler task
155 *
156 * @return string
157 */
158 public function getTaskClassName() {
159 return get_class($this);
160 }
161
162 /**
163 * This method returns the disable status of the task
164 *
165 * @return boolean TRUE if task is disabled, FALSE otherwise
166 */
167 public function isDisabled() {
168 return $this->disabled;
169 }
170
171 /**
172 * This method is used to set the disable status of the task
173 *
174 * @param boolean $flag TRUE if task should be disabled, FALSE otherwise
175 * @return void
176 */
177 public function setDisabled($flag) {
178 if ($flag) {
179 $this->disabled = TRUE;
180 } else {
181 $this->disabled = FALSE;
182 }
183 }
184
185 /**
186 * This method is used to set the timestamp corresponding to the next execution time of the task
187 *
188 * @param integer $timestamp Timestamp of next execution
189 * @return void
190 */
191 public function setExecutionTime($timestamp) {
192 $this->executionTime = intval($timestamp);
193 }
194
195 /**
196 * This method returns the task group (uid) of the task
197 *
198 * @return integer Uid of task group
199 */
200 public function getTaskGroup() {
201 return $this->taskGroup;
202 }
203
204 /**
205 * This method is used to set the task group (uid) of the task
206 *
207 * @param integer $timestamp Uid of task group
208 * @return void
209 */
210 public function setTaskGroup($taskGroup) {
211 $this->taskGroup = intval($taskGroup);
212 }
213
214 /**
215 * This method returns the timestamp corresponding to the next execution time of the task
216 *
217 * @return integer Timestamp of next execution
218 */
219 public function getExecutionTime() {
220 return $this->executionTime;
221 }
222
223 /**
224 * This method is used to set the description of the task
225 *
226 * @param string $description Description
227 * @return void
228 */
229 public function setDescription($description) {
230 $this->description = $description;
231 }
232
233 /**
234 * This method returns the description of the task
235 *
236 * @return string Description
237 */
238 public function getDescription() {
239 return $this->description;
240 }
241
242 /**
243 * Sets the internal reference to the singleton instance of the Scheduler
244 *
245 * @return void
246 */
247 public function setScheduler() {
248 $this->scheduler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Scheduler');
249 }
250
251 /**
252 * Unsets the internal reference to the singleton instance of the Scheduler
253 * This is done before a task is serialized, so that the scheduler instance
254 * is not saved to the database too
255 *
256 * @return void
257 */
258 public function unsetScheduler() {
259 unset($this->scheduler);
260 }
261
262 /**
263 * Registers a single execution of the task
264 *
265 * @param integer $timestamp Timestamp of the next execution
266 */
267 public function registerSingleExecution($timestamp) {
268 /** @var $execution \TYPO3\CMS\Scheduler\Execution */
269 $execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Execution');
270 $execution->setStart($timestamp);
271 $execution->setInterval(0);
272 $execution->setEnd($timestamp);
273 $execution->setCronCmd('');
274 $execution->setMultiple(0);
275 $execution->setIsNewSingleExecution(TRUE);
276 // Replace existing execution object
277 $this->execution = $execution;
278 }
279
280 /**
281 * Registers a recurring execution of the task
282 *
283 * @param integer $start The first date/time where this execution should occur (timestamp)
284 * @param string $interval Execution interval in seconds
285 * @param integer $end The last date/time where this execution should occur (timestamp)
286 * @param boolean $multiple Set to FALSE if multiple executions of this task are not permitted in parallel
287 * @param string $cron_cmd Used like in crontab (minute hour day month weekday)
288 * @return void
289 */
290 public function registerRecurringExecution($start, $interval, $end = 0, $multiple = FALSE, $cron_cmd = '') {
291 /** @var $execution \TYPO3\CMS\Scheduler\Execution */
292 $execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Execution');
293 // Set general values
294 $execution->setStart($start);
295 $execution->setEnd($end);
296 $execution->setMultiple($multiple);
297 if (empty($cron_cmd)) {
298 // Use interval
299 $execution->setInterval($interval);
300 $execution->setCronCmd('');
301 } else {
302 // Use cron syntax
303 $execution->setInterval(0);
304 $execution->setCronCmd($cron_cmd);
305 }
306 // Replace existing execution object
307 $this->execution = $execution;
308 }
309
310 /**
311 * Sets the internal execution object
312 *
313 * @param \TYPO3\CMS\Scheduler\Execution $execution The execution to add
314 */
315 public function setExecution(\TYPO3\CMS\Scheduler\Execution $execution) {
316 $this->execution = $execution;
317 }
318
319 /**
320 * Returns the execution object
321 *
322 * @return \TYPO3\CMS\Scheduler\Execution The internal execution object
323 */
324 public function getExecution() {
325 return $this->execution;
326 }
327
328 /**
329 * Returns the timestamp for next due execution of the task
330 *
331 * @return integer Date and time of the next execution as a timestamp
332 */
333 public function getNextDueExecution() {
334 // NOTE: this call may throw an exception, but we let it bubble up
335 return $this->execution->getNextExecution();
336 }
337
338 /**
339 * Returns TRUE if several runs of the task are allowed concurrently
340 *
341 * @return boolean TRUE if concurrent executions are allowed, FALSE otherwise
342 */
343 public function areMultipleExecutionsAllowed() {
344 return $this->execution->getMultiple();
345 }
346
347 /**
348 * Returns TRUE if an instance of the task is already running
349 *
350 * @return boolean TRUE if an instance is already running, FALSE otherwise
351 */
352 public function isExecutionRunning() {
353 $isRunning = FALSE;
354 $queryArr = array(
355 'SELECT' => 'serialized_executions',
356 'FROM' => 'tx_scheduler_task',
357 'WHERE' => 'uid = ' . intval($this->taskUid),
358 'LIMIT' => 1
359 );
360 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
361 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
362 if (strlen($row['serialized_executions']) > 0) {
363 $isRunning = TRUE;
364 }
365 }
366 $GLOBALS['TYPO3_DB']->sql_free_result($res);
367 return $isRunning;
368 }
369
370 /**
371 * This method adds current execution to the execution list
372 * It also logs the execution time and mode
373 *
374 * @return integer Execution id
375 */
376 public function markExecution() {
377 $queryArr = array(
378 'SELECT' => 'serialized_executions',
379 'FROM' => 'tx_scheduler_task',
380 'WHERE' => 'uid = ' . intval($this->taskUid),
381 'LIMIT' => 1
382 );
383 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
384 $runningExecutions = array();
385 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
386 if (strlen($row['serialized_executions']) > 0) {
387 $runningExecutions = unserialize($row['serialized_executions']);
388 }
389 }
390 $GLOBALS['TYPO3_DB']->sql_free_result($res);
391 // Count the number of existing executions and use that number as a key
392 // (we need to know that number, because it is returned at the end of the method)
393 $numExecutions = count($runningExecutions);
394 $runningExecutions[$numExecutions] = time();
395 // Define the context in which the script is running
396 $context = 'BE';
397 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
398 $context = 'CLI';
399 }
400 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . intval($this->taskUid), array(
401 'serialized_executions' => serialize($runningExecutions),
402 'lastexecution_time' => time(),
403 'lastexecution_context' => $context
404 ));
405 return $numExecutions;
406 }
407
408 /**
409 * Removes given execution from list
410 *
411 * @param integer $executionID Id of the execution to remove.
412 * @param \Exception $failure An exception to signal a failed execution
413 * @return void
414 */
415 public function unmarkExecution($executionID, \Exception $failure = NULL) {
416 // Get the executions for the task
417 $queryArr = array(
418 'SELECT' => 'serialized_executions',
419 'FROM' => 'tx_scheduler_task',
420 'WHERE' => 'uid = ' . intval($this->taskUid),
421 'LIMIT' => 1
422 );
423 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
424 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
425 if (strlen($row['serialized_executions']) > 0) {
426 $runningExecutions = unserialize($row['serialized_executions']);
427 // Remove the selected execution
428 unset($runningExecutions[$executionID]);
429 if (count($runningExecutions) > 0) {
430 // Re-serialize the updated executions list (if necessary)
431 $runningExecutionsSerialized = serialize($runningExecutions);
432 } else {
433 $runningExecutionsSerialized = '';
434 }
435 if ($failure instanceof \Exception) {
436 // Log failed execution
437 $logMessage = 'Task failed to execute successfully. Class: ' . get_class($this) . ', UID: ' . $this->taskUid . '. ' . $failure->getMessage();
438 $this->scheduler->log($logMessage, 1, $failure->getCode());
439 $failure = serialize($failure);
440 } else {
441 $failure = '';
442 }
443 // Save the updated executions list
444 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . intval($this->taskUid), array(
445 'serialized_executions' => $runningExecutionsSerialized,
446 'lastexecution_failure' => $failure
447 ));
448 }
449 }
450 $GLOBALS['TYPO3_DB']->sql_free_result($res);
451 }
452
453 /**
454 * Clears all marked executions
455 *
456 * @return boolean TRUE if the clearing succeeded, FALSE otherwise
457 */
458 public function unmarkAllExecutions() {
459 // Set the serialized executions field to empty
460 $result = $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . intval($this->taskUid), array(
461 'serialized_executions' => ''
462 ));
463 return $result;
464 }
465
466 /**
467 * Saves the details of the task to the database.
468 *
469 * @return boolean
470 */
471 public function save() {
472 return $this->scheduler->saveTask($this);
473 }
474
475 /**
476 * Stops the task, by replacing the execution object by an empty one
477 * NOTE: the task still needs to be saved after that
478 *
479 * @return void
480 */
481 public function stop() {
482 $this->execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Scheduler\\Execution');
483 }
484
485 /**
486 * Removes the task totally from the system.
487 *
488 * @return void
489 */
490 public function remove() {
491 $this->scheduler->removeTask($this);
492 }
493
494 }