ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilCronManager.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
12 {
16  public static function runActiveJobs()
17  {
18  global $ilLog, $ilSetting;
19 
20  // separate log for cron
21  // $this->log->setFilename($_COOKIE["ilClientId"]."_cron.txt");
22 
23  $ilLog->write("CRON - batch start");
24 
25  $ilSetting->set("last_cronjob_start_ts", time());
26 
27  // ilLink::_getStaticLink() should work in crons
28  if(!defined("ILIAS_HTTP_PATH"))
29  {
30  define("ILIAS_HTTP_PATH", ilUtil::_getHttpPath());
31  }
32 
33  // system
34  foreach(self::getCronJobData(null, false) as $row)
35  {
36  $job = self::getJobInstanceById($row["job_id"]);
37  if($job)
38  {
39  // #18411 - we are NOT using the initial job data as it might be outdated at this point
40  self::runJob($job);
41  }
42  }
43 
44  // plugins
45  foreach(self::getPluginJobs(true) as $item)
46  {
47  self::runJob($item[0], $item[1]);
48  }
49 
50  $ilLog->write("CRON - batch end");
51  }
52 
59  public static function runJobManual($a_job_id)
60  {
61  global $ilLog;
62 
63  $result = false;
64 
65  $ilLog->write("CRON - manual start (".$a_job_id.")");
66 
67  $job = self::getJobInstanceById($a_job_id);
68  if($job)
69  {
70  if($job->isManuallyExecutable())
71  {
72  $result = self::runJob($job, null, true);
73  }
74  else
75  {
76  $ilLog->write("CRON - job ".$a_job_id." is not intended to be executed manually");
77  }
78  }
79  else
80  {
81  $ilLog->write("CRON - job ".$a_job_id." seems invalid or is inactive");
82  }
83 
84  $ilLog->write("CRON - manual end (".$a_job_id.")");
85 
86  return $result;
87  }
88 
97  protected static function runJob(ilCronJob $a_job, array $a_job_data = null, $a_manual = false)
98  {
99  global $ilLog, $ilDB;
100 
101  $did_run = false;
102 
103  if($a_job_data === null)
104  {
105  // aquire "fresh" job (status) data
106  $a_job_data = array_pop(self::getCronJobData($a_job->getId()));
107  }
108 
109  if($a_job)
110  {
111  include_once "Services/Cron/classes/class.ilCronJobResult.php";
112 
113  // already running?
114  if($a_job_data["alive_ts"])
115  {
116  $ilLog->write("CRON - job ".$a_job_data["job_id"]." still running");
117 
118  $cut = 60*60*3; // 3h
119 
120  // is running (and has not pinged) for 3 hours straight, we assume it crashed
121  if(time()-$a_job_data["alive_ts"] > $cut)
122  {
123  $ilDB->manipulate("UPDATE cron_job SET".
124  " running_ts = ".$ilDB->quote(0, "integer").
125  " , alive_ts = ".$ilDB->quote(0, "integer").
126  " WHERE job_id = ".$ilDB->quote($a_job_data["job_id"], "text"));
127 
128  self::deactivateJob($a_job); // #13082
129 
130  $result = new ilCronJobResult();
132  $result->setCode("job_auto_deactivation_time_limit");
133  $result->setMessage("Cron job deactivated because it has been inactive for 3 hours");
134 
135  if(!$a_manual)
136  {
137  self::sendNotification($a_job, $result);
138  }
139 
140  self::updateJobResult($a_job, $result, $a_manual);
141 
142  $ilLog->write("CRON - job ".$a_job_data["job_id"]." deactivated (assumed crash)");
143  }
144  }
145  // initiate run?
146  else if($a_job->isActive($a_job_data["job_result_ts"],
147  $a_job_data["schedule_type"], $a_job_data["schedule_value"], $a_manual))
148  {
149  $ilLog->write("CRON - job ".$a_job_data["job_id"]." started");
150 
151  $ilDB->manipulate("UPDATE cron_job SET".
152  " running_ts = ".$ilDB->quote(time(), "integer").
153  " , alive_ts = ".$ilDB->quote(time(), "integer").
154  " WHERE job_id = ".$ilDB->quote($a_job_data["job_id"], "text"));
155 
156  $ts_in = self::getMicrotime();
157  $result = $a_job->run();
158  $ts_dur = self::getMicrotime()-$ts_in;
159 
160  // no proper result
161  if(!$result instanceof ilCronJobResult)
162  {
163  $result = new ilCronJobResult();
165  $result->setCode("job_no_result");
166  $result->setMessage("Cron job did not return a proper result");
167 
168  if(!$a_manual)
169  {
170  self::sendNotification($a_job, $result);
171  }
172 
173  $ilLog->write("CRON - job ".$a_job_data["job_id"]." no result");
174  }
175  // no valid configuration, job won't work
177  {
178  self::deactivateJob($a_job);
179 
180  if(!$a_manual)
181  {
182  self::sendNotification($a_job, $result);
183  }
184 
185  $ilLog->write("CRON - job ".$a_job_data["job_id"]." invalid configuration");
186  }
187  // success!
188  else
189  {
190  $did_run = true;
191  }
192 
193  $result->setDuration($ts_dur);
194 
195  self::updateJobResult($a_job, $result, $a_manual);
196 
197  $ilDB->manipulate("UPDATE cron_job SET".
198  " running_ts = ".$ilDB->quote(0, "integer").
199  " , alive_ts = ".$ilDB->quote(0, "integer").
200  " WHERE job_id = ".$ilDB->quote($a_job_data["job_id"], "text"));
201 
202  $ilLog->write("CRON - job ".$a_job_data["job_id"]." finished");
203  }
204  else
205  {
206  $ilLog->write("CRON - job ".$a_job_data["job_id"]." returned status inactive");
207  }
208  }
209 
210  return $did_run;
211  }
212 
219  public static function getJobInstanceById($a_job_id)
220  {
221  global $ilLog, $ilPluginAdmin;
222 
223  // plugin
224  if(substr($a_job_id, 0, 4) == "pl__")
225  {
226  $parts = explode("__", $a_job_id);
227  $pl_name = $parts[1];
228  $job_id = $parts[2];
229  if($ilPluginAdmin->isActive(IL_COMP_SERVICE, "Cron", "crnhk", $pl_name))
230  {
231  $plugin_obj = $ilPluginAdmin->getPluginObject(IL_COMP_SERVICE,
232  "Cron", "crnhk", $pl_name);
233  $job = $plugin_obj->getCronJobInstance($job_id);
234  if($job instanceof ilCronJob)
235  {
236  // should never happen but who knows...
237  if(!sizeof(ilCronManager::getCronJobData($job_id)))
238  {
239  // as job is not "imported" from xml
241  }
242  return $job;
243  }
244  }
245 
246  return null;
247  }
248  // system
249  else
250  {
251  $job_data = array_pop(self::getCronJobData($a_job_id));
252  if($job_data["job_id"] == $a_job_id)
253  {
254  return self::getJobInstance($job_data["job_id"], $job_data["component"],
255  $job_data["class"], $job_data["path"]);
256  }
257  }
258 
259  $ilLog->write("CRON - job ".$a_job_id." seems invalid or is inactive");
260  }
261 
270  public static function getJobInstance($a_id, $a_component, $a_class, $a_path = null)
271  {
272  global $ilLog;
273 
274  if(!$a_path)
275  {
276  $a_path = $a_component."/classes/";
277  }
278  $class_file = $a_path."class.".$a_class.".php";
279  if(file_exists($class_file))
280  {
281  include_once $class_file;
282  if(class_exists($a_class))
283  {
284  $job = new $a_class();
285  if($job instanceof ilCronJob)
286  {
287  if($job->getId() == $a_id)
288  {
289  return $job;
290  }
291  else
292  {
293  $mess .= " - job id mismatch";
294  }
295  }
296  else
297  {
298  $mess .= " - does not extend ilCronJob";
299  }
300  }
301  else
302  {
303  $mess = "- class not found in file";
304  }
305  }
306  else
307  {
308  $mess = " - class file not found";
309  }
310 
311  $ilLog->write("Cron XML - Job ".$a_id." in class ".$a_class." (".
312  $class_file.") is invalid.".$mess);
313  }
314 
321  protected static function sendNotification(ilCronJob $a_job, $a_message)
322  {
323  // :TODO:
324  }
325 
326  public static function createDefaultEntry(ilCronJob $a_job, $a_component, $a_class, $a_path)
327  {
328  global $ilDB, $ilLog, $ilSetting;
329 
330  // already exists?
331  $sql = "SELECT job_id, schedule_type FROM cron_job".
332  " WHERE component = ".$ilDB->quote($a_component, "text").
333  " AND job_id = ".$ilDB->quote($a_job->getId(), "text");
334  $set = $ilDB->query($sql);
335  $row = $ilDB->fetchAssoc($set);
336  $job_exists = ($row["job_id"] == $a_job->getId());
337  $schedule_type = $row["schedule_type"];
338 
339  // new job
340  if(!$job_exists)
341  {
342  $sql = "INSERT INTO cron_job (job_id, component, class, path)".
343  " VALUES (".$ilDB->quote($a_job->getId(), "text").", ".
344  $ilDB->quote($a_component, "text").", ".
345  $ilDB->quote($a_class, "text").", ".
346  $ilDB->quote($a_path, "text").")";
347  $ilDB->manipulate($sql);
348 
349  $ilLog->write("Cron XML - Job ".$a_job->getId()." in class ".$a_class.
350  " added.");
351 
352  // only if flexible
353  self::updateJobSchedule($a_job,
354  $a_job->getDefaultScheduleType(),
355  $a_job->getDefaultScheduleValue());
356 
357  // #12221
358  if(!is_object($ilSetting))
359  {
360  include_once "Services/Administration/classes/class.ilSetting.php";
361  $ilSetting = new ilSetting();
362  }
363 
364  if($a_job->hasAutoActivation())
365  {
366  self::activateJob($a_job);
367  }
368  else
369  {
370  // to overwrite dependent settings
371  $a_job->activationWasToggled(false);
372  }
373  }
374  // existing job - but schedule is flexible now
375  else if($a_job->hasFlexibleSchedule() && !$schedule_type)
376  {
377  self::updateJobSchedule($a_job,
378  $a_job->getDefaultScheduleType(),
379  $a_job->getDefaultScheduleValue());
380  }
381  // existing job - but schedule is static now
382  else if(!$a_job->hasFlexibleSchedule() && $schedule_type)
383  {
384  self::updateJobSchedule($a_job, null, null);
385  }
386  }
387 
396  public static function updateFromXML($a_component, $a_id, $a_class, $a_path = null)
397  {
398  global $ilDB;
399 
400  if(!$ilDB->tableExists("cron_job"))
401  {
402  return;
403  }
404 
405  // only if job seems valid
406  $job = self::getJobInstance($a_id, $a_component, $a_class, $a_path);
407  if($job)
408  {
409  self::createDefaultEntry($job, $a_component, $a_class, $a_path);
410  }
411  }
412 
419  public static function clearFromXML($a_component, array $a_xml_job_ids)
420  {
421  global $ilDB, $ilLog;
422 
423  if(!$ilDB->tableExists("cron_job"))
424  {
425  return;
426  }
427 
428  // gather existing jobs
429  $all_jobs = array();
430  $sql = "SELECT job_id FROM cron_job".
431  " WHERE component = ".$ilDB->quote($a_component, "text");
432  $set = $ilDB->query($sql);
433  while($row = $ilDB->fetchAssoc($set))
434  {
435  $all_jobs[] = $row["job_id"];
436  }
437 
438  if(sizeof($all_jobs))
439  {
440  if(sizeof($a_xml_job_ids))
441  {
442  // delete obsolete job data
443  foreach($all_jobs as $job_id)
444  {
445  if(!in_array($job_id, $a_xml_job_ids))
446  {
447  $ilDB->manipulate("DELETE FROM cron_job".
448  " WHERE component = ".$ilDB->quote($a_component, "text").
449  " AND job_id = ".$ilDB->quote($job_id, "text"));
450 
451  $ilLog->write("Cron XML - Job ".$job_id." in class ".$a_component.
452  " deleted.");
453  }
454  }
455  }
456  else
457  {
458  $ilDB->manipulate("DELETE FROM cron_job".
459  " WHERE component = ".$ilDB->quote($a_component, "text"));
460 
461  $ilLog->write("Cron XML - All jobs deleted for ".$a_component." as component is inactive.");
462  }
463  }
464  }
465 
466  public static function getPluginJobs($a_only_active = false)
467  {
468  global $ilPluginAdmin;
469 
470  $res = array();
471 
472  foreach($ilPluginAdmin->getActivePluginsForSlot(IL_COMP_SERVICE, "Cron", "crnhk") as $pl_name)
473  {
474  $plugin_obj = $ilPluginAdmin->getPluginObject(IL_COMP_SERVICE, "Cron", "crnhk", $pl_name);
475 
476  foreach((array)$plugin_obj->getCronJobInstances() as $job)
477  {
478  $item = array_pop(ilCronManager::getCronJobData($job->getId()));
479  if(!sizeof($item))
480  {
481  // as job is not "imported" from xml
483  }
484 
485  $item = array_pop(ilCronManager::getCronJobData($job->getId()));
486 
487  // #17941
488  if(!$a_only_active ||
489  $item["job_status"] == 1)
490  {
491  $res[$job->getId()] = array($job, $item);
492  }
493  }
494  }
495 
496  return $res;
497  }
498 
506  public static function getCronJobData($a_id = null, $a_include_inactive = true)
507  {
508  global $ilDB;
509 
510  $res = array();
511 
512  if($a_id && !is_array($a_id))
513  {
514  $a_id = array($a_id);
515  }
516 
517  $sql = "SELECT * FROM cron_job";
518 
519  $where = array();
520  if($a_id)
521  {
522  $where[] = $ilDB->in("job_id", $a_id, "", "text");
523  }
524  else
525  {
526  $where[] = "class <> ".$ilDB->quote(IL_COMP_PLUGIN, "text");
527  }
528  if(!$a_include_inactive)
529  {
530  $where[] = "job_status = ".$ilDB->quote(1, "integer");
531  }
532  if(sizeof($where))
533  {
534  $sql .= " WHERE ".implode(" AND ", $where);
535  }
536 
537  // :TODO: discuss job execution order
538  $sql .= " ORDER BY job_id";
539 
540  $set = $ilDB->query($sql);
541  while($row = $ilDB->fetchAssoc($set))
542  {
543  $res[] = $row;
544  }
545 
546  return $res;
547  }
548 
554  public static function resetJob(ilCronJob $a_job)
555  {
556  global $ilDB;
557 
558  include_once "Services/Cron/classes/class.ilCronJobResult.php";
559  $result = new ilCronJobResult();
561  $result->setCode("job_manual_reset");
562  $result->setMessage("Cron job re-activated by admin");
563  self::updateJobResult($a_job, $result, true);
564 
565  $ilDB->manipulate("UPDATE cron_job".
566  " SET running_ts = ".$ilDB->quote(0, "integer").
567  " , alive_ts = ".$ilDB->quote(0, "integer").
568  " , job_result_ts = ".$ilDB->quote(0, "integer").
569  " WHERE job_id = ".$ilDB->quote($a_job->getId(), "text"));
570 
571  self::activateJob($a_job, true);
572  }
573 
580  public static function activateJob(ilCronJob $a_job, $a_manual = false)
581  {
582  global $ilDB, $ilUser;
583 
584  $user_id = $a_manual ? $ilUser->getId() : 0;
585 
586  $sql = "UPDATE cron_job SET ".
587  " job_status = ".$ilDB->quote(1, "integer").
588  " , job_status_user_id = ".$ilDB->quote($user_id, "integer").
589  " , job_status_type = ".$ilDB->quote($a_manual, "integer").
590  " , job_status_ts = ".$ilDB->quote(time(), "integer").
591  " WHERE job_id = ".$ilDB->quote($a_job->getId(), "text");
592  $ilDB->manipulate($sql);
593 
594  $a_job->activationWasToggled(true);
595  }
596 
603  public static function deactivateJob(ilCronJob $a_job, $a_manual = false)
604  {
605  global $ilDB, $ilUser;
606 
607  $user_id = $a_manual ? $ilUser->getId() : 0;
608 
609  $sql = "UPDATE cron_job SET ".
610  " job_status = ".$ilDB->quote(0, "integer").
611  " , job_status_user_id = ".$ilDB->quote($user_id, "integer").
612  " , job_status_type = ".$ilDB->quote($a_manual, "integer").
613  " , job_status_ts = ".$ilDB->quote(time(), "integer").
614  " WHERE job_id = ".$ilDB->quote($a_job->getId(), "text");
615  $ilDB->manipulate($sql);
616 
617  $a_job->activationWasToggled(false);
618  }
619 
626  public static function isJobActive($a_job_id)
627  {
628  $job = self::getCronJobData($a_job_id);
629  if((bool)$job[0]["job_status"])
630  {
631  return true;
632  }
633  return false;
634  }
635 
642  public static function isJobInactive($a_job_id)
643  {
644  $job = self::getCronJobData($a_job_id);
645  if(!(bool)$job[0]["job_status"])
646  {
647  return true;
648  }
649  return false;
650  }
651 
659  protected static function updateJobResult(ilCronJob $a_job, ilCronJobResult $a_result, $a_manual = false)
660  {
661  global $ilDB, $ilUser;
662 
663  $user_id = $a_manual ? $ilUser->getId() : 0;
664 
665  $sql = "UPDATE cron_job SET ".
666  " job_result_status = ".$ilDB->quote($a_result->getStatus(), "integer").
667  " , job_result_user_id = ".$ilDB->quote($user_id, "integer").
668  " , job_result_code = ".$ilDB->quote($a_result->getCode(), "text").
669  " , job_result_message = ".$ilDB->quote($a_result->getMessage(), "text").
670  " , job_result_type = ".$ilDB->quote($a_manual, "integer").
671  " , job_result_ts = ".$ilDB->quote(time(), "integer").
672  " , job_result_dur = ".$ilDB->quote($a_result->getDuration()*1000, "integer").
673  " WHERE job_id = ".$ilDB->quote($a_job->getId(), "text");
674  $ilDB->manipulate($sql);
675  }
676 
684  public static function updateJobSchedule(ilCronJob $a_job, $a_schedule_type, $a_schedule_value)
685  {
686  global $ilDB;
687 
688  if($a_schedule_type === null ||
689  ($a_job->hasFlexibleSchedule() &&
690  in_array($a_schedule_type, $a_job->getValidScheduleTypes())))
691  {
692  $sql = "UPDATE cron_job SET ".
693  " schedule_type = ".$ilDB->quote($a_schedule_type, "integer").
694  " , schedule_value = ".$ilDB->quote($a_schedule_value, "integer").
695  " WHERE job_id = ".$ilDB->quote($a_job->getId(), "text");
696  $ilDB->manipulate($sql);
697  }
698  }
699 
705  protected static function getMicrotime()
706  {
707  list($usec, $sec) = explode(" ", microtime());
708  return ((float)$usec + (float)$sec);
709  }
710 
716  public static function ping($a_job_id)
717  {
718  global $ilDB;
719 
720  $ilDB->manipulate("UPDATE cron_job SET ".
721  " alive_ts = ".$ilDB->quote(time(), "integer").
722  " WHERE job_id = ".$ilDB->quote($a_job_id, "text"));
723  }
724 }
725 
726 ?>
static getJobInstance($a_id, $a_component, $a_class, $a_path=null)
Get job instance (by job data)
static runActiveJobs()
Run all active jobs.
run()
Run job.
ILIAS Setting Class.
static updateFromXML($a_component, $a_id, $a_class, $a_path=null)
Process data from module.xml/service.xml.
static sendNotification(ilCronJob $a_job, $a_message)
Send notification to admin about job event(s)
getValidScheduleTypes()
Get all available schedule types.
$result
getId()
Get id.
Cron job application base class.
static deactivateJob(ilCronJob $a_job, $a_manual=false)
Deactivate cron job.
static runJob(ilCronJob $a_job, array $a_job_data=null, $a_manual=false)
Run single cron job (internal)
const IL_COMP_PLUGIN
static activateJob(ilCronJob $a_job, $a_manual=false)
Activate cron job.
static isJobInactive($a_job_id)
Check if given job is currently inactive.
static runJobManual($a_job_id)
Run single job manually.
activationWasToggled($a_currently_active)
Cron job status was changed.
static resetJob(ilCronJob $a_job)
Reset job.
Cron management.
static getCronJobData($a_id=null, $a_include_inactive=true)
Get cron job configuration/execution data.
static getJobInstanceById($a_job_id)
Get job instance (by job id)
hasFlexibleSchedule()
Can the schedule be configured?
static createDefaultEntry(ilCronJob $a_job, $a_component, $a_class, $a_path)
isActive($a_ts_last_run, $a_schedule_type, $a_schedule_value, $a_manual=false)
Is job currently active?
static updateJobResult(ilCronJob $a_job, ilCronJobResult $a_result, $a_manual=false)
Save job result.
getDefaultScheduleType()
Get schedule type.
static _getHttpPath()
static ping($a_job_id)
Keep cron job alive.
global $ilUser
Definition: imgupload.php:15
global $ilSetting
Definition: privfeed.php:40
static getPluginJobs($a_only_active=false)
static getMicrotime()
Get current microtime.
static clearFromXML($a_component, array $a_xml_job_ids)
Clear job data.
global $ilDB
getDefaultScheduleValue()
Get schedule value.
static updateJobSchedule(ilCronJob $a_job, $a_schedule_type, $a_schedule_value)
Update job schedule.
Cron job result data container.
static isJobActive($a_job_id)
Check if given job is currently active.
hasAutoActivation()
Is to be activated on "installation".
const IL_COMP_SERVICE