HCE Project DC service web UI  0.2
Hierarchical Cluster Engine DC service web UI
 All Classes Namespaces Files Functions Variables Pages
Job.php
Go to the documentation of this file.
1 <?php
2 
3 class Job extends Jobs implements JobInterface
4 {
9  protected static $logClass = "application.modules.job.models.Job";
10 
15  private $__finishMessage = array();
16 
21  private $__log = array();
22 
27  public $rollbackOpenTransaction = true;
28 
32  public static function model($className=__CLASS__)
33  {
34  return parent::model($className);
35  }
36 
40  public function rules()
41  {
42  return array_merge(parent::rules(), array(
43  array('crontab', 'validateCrontab')
44  ));
45  }
46 
50  public function identifiers()
51  {
52  return array();
53  }
54 
59  public function getJobStatusId()
60  {
61  return $this->job_status_id;
62  }
63 
67  public function validateCrontab($attribute, $params)
68  {
69  if ($this->crontab)
70  {
71  try
72  {
73  $cron = CronExpression::factory($this->crontab);
74  }
75  catch (InvalidArgumentException $e)
76  {
77  $this->addError('crontab', 'crontab notation parsing failed');
78  }
79  }
80  }
81 
85  public function startNow()
86  {
87  $this->planned_time = $this->timestampToDatabaseDate(time());
88  }
89 
93  public function calculateNextPlannedTime()
94  {
95  if ($this->crontab)
96  {
97  $cron = CronExpression::factory($this->crontab);
98  $this->planned_time = $this->timestampToDatabaseDate($cron->getNextRunDate()->getTimestamp());
99  }
100  }
101 
105  public function beforeSave()
106  {
107  $jobData = $this->getJobData();
108  $identifiers = array_flip($this->identifiers());
109  if($identifiers && is_array($jobData))
110  {
111  foreach ($jobData as $name => $value)
112  {
113  if (isset($identifiers[$name]))
114  {
115  $column = $identifiers[$name];
116  $this->$column = $value;
117  }
118  }
119  }
120 
121  $this->update_time = $this->timestampToDatabaseDate();
122  if (!$this->create_time)
123  {
124  $this->create_time = $this->update_time;
125  }
126  $this->job_data = json_encode($jobData);
127 
128  //Yii::trace("job data in before save {$this->id}: {$this->job_data}");
129 
130  return parent::beforeSave();
131  }
132 
136  public function afterConstruct()
137  {
138  if ($this->scenario != 'search')
139  {
140  $this->job_class = get_class($this);
141  $this->job_status_id = JobStatus::ENQUEUED;
142  }
144  }
145 
149  public function beforeExecute()
150  {
151  $this->changeToRunningStatus();
152  if (!$this->save())
153  throw new Exception("Unable to save job in beforeExecute");
154  }
155 
159  public function onSuccess()
160  {
161 
162  }
163 
167  public function onError()
168  {
169 
170  }
171 
175  public function _onError()
176  {
177  try
178  {
179  $this->onError();
180  }
181  catch (Exception $ex)
182  {
183  Yii::log("Exception during job onError (ID {$this->id}): ".$ex->getMessage(), CLogger::LEVEL_ERROR);
184  }
185  }
186 
191  public function executeDirect()
192  {
193  $this->changeToRunningStatus();
194  $this->execute();
195  return $this->job_status_id;
196  }
197 
202  public function executeAsync() {
203  $command = './yiic job execJob --name=' . $this->job_class . ' --id=' . $this->id;
204  shell_exec(sprintf('%s > /dev/null 2>&1 &', $command));
205  return true;
206  }
207 
211  public function execute()
212  {
213  Yii::trace("execute", self::$logClass);
214 
215  try
216  {
217  $result = $this->_execute();
218  $errors = error_get_last();
219  if (!empty($errors)) {
220  $result = false;
221  $this->__finishMessage = array(
222  'job_data' => $this->job_data,
223  'errors' => $errors
224  );
225  }
226 
227  //set default result when child execute did not set another status than RUNNING
228  if ($this->job_status_id == JobStatus::RUNNING)
229  {
230  $this->job_status_id = $result ? JobStatus::SUCCESS : JobStatus::ERROR;
231  }
232 
233  $this->onSuccess();
234  }
235  catch (Exception $e)
236  {
237  $this->job_status_id = JobStatus::EXCEPTION;
238  $this->__finishMessage = array(
239  'job_data' => $this->job_data,
240  'errors' => $e->getMessage(),
241  'trace' => $e->getTrace()
242  );
243 
244  $this->_onError();
245  }
246 
247  //in case the job has left a transaction, we rollback here!
248  if ($this->rollbackOpenTransaction)
249  {
250  if ($transaction = Yii::app()->db->getCurrentTransaction())
251  $transaction->rollback();
252  }
253 
254  $this->afterExecute();
255  }
256 
260  public function afterExecute()
261  {
262  Yii::trace("after execute, jobStatus = {$this->job_status_id}", self::$logClass);
263 
264  $this->logResult();
265  $this->updateJob();
266  }
267 
271  public function updateJob() {
272  $job = Job::model()->findByPk($this->id);
273  $job->job_status_id = $this->job_status_id;
274  $time = $this->timestampToDatabaseDate();
275  $job->update_time = $time;
276  $job->finish_time = $time;
277  $job->save();
278  }
279 
284  public function getFinishMessage()
285  {
286  $this->__finishMessage['log'] = $this->getLog();
287  return $this->__finishMessage;
288  }
289 
294  public function log($message)
295  {
296  $this->__log[] = $message;
297  }
298 
303  public function getLog()
304  {
305  return $this->__log;
306  }
307 
312  public function getJobData()
313  {
314  }
315 
320  public function setJobData($data)
321  {
322 
323  if (!is_array($data))
324  return;
325 
326  foreach ($data as $attribute => $value)
327  {
328  $this->$attribute = $value;
329  }
330  }
331 
337  public function isDuplicateOf($job)
338  {
339  return false;
340  }
341 
345  protected function _execute()
346  {
347  throw new Exception("Illegal call to _execute. This method should have been overwritten");
348  }
349 
355  protected function timestampToDatabaseDate($timestamp = null)
356  {
357  if ($timestamp === null)
358  {
359  $timestamp = time();
360  }
361 
362  return date("Y-m-d G:i:s", $timestamp);
363  }
364 
368  protected function beforeValidate()
369  {
370  if ($this->planned_time === null)
371  {
372  $this->calculateNextPlannedTime();
373  if ($this->planned_time === null)
374  $this->planned_time = $this->timestampToDatabaseDate();
375  }
376 
377  return parent::beforeValidate();
378  }
379 
383  protected function afterFind()
384  {
385  //Yii::trace("Decoding {$this->job_data}");
386  $this->setJobData(json_decode($this->job_data, true));
388  }
389 
393  protected function instantiate($attributes)
394  {
395  if ($class = $attributes['job_class'])
396  {
397  return new $class(null);
398  }
399  else
401  }
402 
406  protected function changeToRunningStatus()
407  {
408  $this->start_time = $this->timestampToDatabaseDate();
409  $this->job_status_id = JobStatus::RUNNING;
410  }
411 
415  protected function logResult()
416  {
417  Yii::trace("log result", self::$logClass);
418  $log = new JobLog();
419  $log->finish_message = $this->getFinishMessage();
420  $log->create_time = $this->timestampToDatabaseDate();
421  $log->job_id = $this->id;
422 
423  if (!$log->save())
424  return Yii::log('Saving a job log failed: ' . var_export($log->errors, true), 'error');
425 
426  if ($this->job_status_id == JobStatus::EXCEPTION)
427  Yii::log("Running job {$this->job_class} resulted in an exception: " . print_r($this->extractErrorMessageFromFinishMessage($log), true) . ". Please see log ID {$log->id} for details", 'error');
428 
429  if ($this->job_status_id == JobStatus::ERROR)
430  Yii::log("Running job {$this->job_class} resulted in an error: " . print_r($this->extractErrorMessageFromFinishMessage($log), true) . " Please see log ID {$log->id} for details", 'error');
431  }
432 
439  {
440  $finish = $log->finish_message;
441  if (is_string($finish))
442  $finish = json_decode($finish, true);
443 
444  if (isset($finish['errors']))
445  return $finish['errors'];
446 
447  return '';
448  }
449 }