QueuedJobDescriptor::activateOnQueue()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 0
1
<?php
2
3
namespace Symbiote\QueuedJobs\DataObjects;
4
5
use SilverStripe\Assets\Filesystem;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Forms\DropdownField;
8
use SilverStripe\ORM\DataObject;
9
use Symbiote\QueuedJobs\Services\QueuedJob;
10
use SilverStripe\Security\Permission;
11
12
/**
13
 * A QueuedJobDescriptor is the stored representation of a piece of work that could take a while to execute,
14
 * because of which it is desireable to not have it executing in parallel to other jobs.
15
 *
16
 * A queued job should always attempt to report how many potential dataobjects will be affected by being executed;
17
 * this will determine which queue it is placed within so that some shorter jobs can execute immediately without needing
18
 * to wait for a potentially long running job.
19
 *
20
 * @property string $JobTitle Name of job
21
 * @property string $Signature Unique identifier for this job instance
22
 * @property string $Implementation Classname of underlying job
23
 * @property string $StartAfter Don't start until this date, if set
24
 * @property string $JobStarted When this job was started
25
 * @property string $JobFinished When this job was finished
26
 * @property int $TotalSteps Number of steps
27
 * @property int $StepsProcessed Number of completed steps
28
 * @property int $LastProcessedCount Number at which StepsProcessed was last checked for stalled jobs
29
 * @property int $ResumeCounts Number of times this job has been resumed
30
 * @property string $SavedJobData serialised data for the job to use as storage
31
 * @property string $SavedJobMessages List of messages saved for this job
32
 * @property string $JobStatus Status of this job
33
 * @property string $JobType Type of job
34
 *
35
 * @method Member RunAs() Member to run this job as
36
 *
37
 * @author Marcus Nyeholt <[email protected]>
38
 * @license BSD http://silverstripe.org/bsd-license/
39
 */
40
class QueuedJobDescriptor extends DataObject
41
{
42
    /**
43
     * {@inheritDoc}
44
     * @var string
45
     */
46
    private static $table_name = 'QueuedJobDescriptor';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $table_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
47
48
    /**
49
     * @var array
50
     */
51
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
52
        'JobTitle' => 'Varchar(255)',
53
        'Signature' => 'Varchar(64)',
54
        'Implementation' => 'Varchar(255)',
55
        'StartAfter' => 'DBDatetime',
56
        'JobStarted' => 'DBDatetime',
57
        'JobRestarted' => 'DBDatetime',
58
        'JobFinished' => 'DBDatetime',
59
        'TotalSteps' => 'Int',
60
        'StepsProcessed' => 'Int',
61
        'LastProcessedCount' => 'Int(-1)', // -1 means never checked, 0 means checked but no work is done
62
        'ResumeCounts' => 'Int',
63
        'SavedJobData' => 'Text',
64
        'SavedJobMessages' => 'Text',
65
        'JobStatus' => 'Varchar(16)',
66
        'JobType' => 'Varchar(16)',
67
    );
68
69
    /**
70
     * @var array
71
     */
72
    private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
73
        'RunAs' => 'SilverStripe\\Security\\Member',
74
    );
75
76
    /**
77
     * @var array
78
     */
79
    private static $defaults = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $defaults is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
80
        'JobStatus' => 'New',
81
        'ResumeCounts' => 0,
82
        'LastProcessedCount' => -1 // -1 means never checked, 0 means checked and none were processed
83
    );
84
85
    /**
86
     * @var array
87
     */
88
    private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
89
        'JobStatus' => true,
90
        'StartAfter' => true,
91
        'Signature' => true
92
    );
93
94
    /**
95
     * @var array
96
     */
97
    private static $casting = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $casting is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
98
        'Messages' => 'HTMLText'
99
    );
100
101
    /**
102
     * @var array
103
     */
104
    private static $searchable_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $searchable_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
105
        'JobTitle',
106
    );
107
108
    /**
109
     * @var string
110
     */
111
    private static $default_sort = 'Created DESC';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $default_sort is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
112
113
    public function requireDefaultRecords()
114
    {
115
        parent::requireDefaultRecords();
116
        $this->getJobDir();
117
    }
118
119
    /**
120
     * @return array
121
     */
122
    public function summaryFields()
123
    {
124
        return array(
125
            'JobTitle' => _t('QueuedJobs.TABLE_TITLE', 'Title'),
126
            'Created' => _t('QueuedJobs.TABLE_ADDE', 'Added'),
127
            'JobStarted' => _t('QueuedJobs.TABLE_STARTED', 'Started'),
128
//			'JobRestarted' => _t('QueuedJobs.TABLE_RESUMED', 'Resumed'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
129
            'StartAfter' => _t('QueuedJobs.TABLE_START_AFTER', 'Start After'),
130
            'JobType'   => _t('QueuedJobs.JOB_TYPE', 'Job Type'),
131
            'JobStatus' => _t('QueuedJobs.TABLE_STATUS', 'Status'),
132
            'Messages' => _t('QueuedJobs.TABLE_MESSAGES', 'Message'),
133
            'StepsProcessed' => _t('QueuedJobs.TABLE_NUM_PROCESSED', 'Done'),
134
            'TotalSteps' => _t('QueuedJobs.TABLE_TOTAL', 'Total'),
135
        );
136
    }
137
138
    /**
139
     * Pause this job, but only if it is waiting, running, or init
140
     *
141
     * @param bool $force Pause this job even if it's not waiting, running, or init
142
     * @return bool Return true if this job was paused
143
     */
144
    public function pause($force = false)
145
    {
146
        if ($force || in_array(
147
            $this->JobStatus,
148
            array(QueuedJob::STATUS_WAIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_INIT)
149
        )) {
150
            $this->JobStatus = QueuedJob::STATUS_PAUSED;
151
            $this->write();
152
            return true;
153
        }
154
        return false;
155
    }
156
157
    /**
158
     * Resume this job and schedules it for execution
159
     *
160
     * @param bool $force Resume this job even if it's not paused or broken
161
     * @return bool Return true if this job was resumed
162
     */
163
    public function resume($force = false)
164
    {
165
        if ($force || in_array($this->JobStatus, array(QueuedJob::STATUS_PAUSED, QueuedJob::STATUS_BROKEN))) {
166
            $this->JobStatus = QueuedJob::STATUS_WAIT;
167
            $this->ResumeCounts++;
168
            $this->write();
169
            singleton('Symbiote\\QueuedJobs\\Services\\QueuedJobService')->startJob($this);
170
            return true;
171
        }
172
        return false;
173
    }
174
175
    /**
176
     * Restarts this job via a forced resume
177
     */
178
    public function restart()
179
    {
180
        $this->resume(true);
181
    }
182
183
    /**
184
     * Called to indicate that the job is ready to be run on the queue. This is done either as the result of
185
     * creating the job and adding it, or when resuming.
186
     */
187
    public function activateOnQueue()
188
    {
189
        // if it's an immediate job, lets cache it to disk to be picked up later
190
        if ($this->JobType == QueuedJob::IMMEDIATE
191
            && !Config::inst()->get('Symbiote\\QueuedJobs\\Services\\QueuedJobService', 'use_shutdown_function')
192
        ) {
193
            touch($this->getJobDir() . '/queuedjob-' . $this->ID);
194
        }
195
    }
196
197
    /**
198
     * Gets the path to the queuedjob cache directory
199
     *
200
     * @return string
201
     */
202
    protected function getJobDir()
203
    {
204
        // make sure our temp dir is in place. This is what will be inotify watched
205
        $jobDir = Config::inst()->get('Symbiote\\QueuedJobs\\Services\\QueuedJobService', 'cache_dir');
206
        if ($jobDir{0} != '/') {
207
            $jobDir = TEMP_FOLDER . '/' . $jobDir;
208
        }
209
210
        if (!is_dir($jobDir)) {
211
            Filesystem::makeFolder($jobDir);
212
        }
213
        return $jobDir;
214
    }
215
216
    public function execute()
217
    {
218
        $service = singleton('Symbiote\\QueuedJobs\\Services\\QueuedJobService');
219
        $service->runJob($this->ID);
220
    }
221
222
    /**
223
     * Called when the job has completed and we want to cleanup anything the descriptor has lying around
224
     * in caches or the like.
225
     */
226
    public function cleanupJob()
227
    {
228
        // remove the job's temp file if it exists
229
        $tmpFile = $this->getJobDir() . '/queuedjob-' . $this->ID;
230
        if (file_exists($tmpFile)) {
231
            unlink($tmpFile);
232
        }
233
    }
234
235
    public function onBeforeDelete()
236
    {
237
        parent::onBeforeDelete();
238
        $this->cleanupJob();
239
    }
240
241
    /**
242
     * @return string|void
243
     */
244
    public function getMessages()
245
    {
246
        if (strlen($this->SavedJobMessages)) {
247
            $msgs = @unserialize($this->SavedJobMessages);
248
            return is_array($msgs) ? '<ul><li>'.implode('</li><li>', $msgs).'</li></ul>' : '';
249
        }
250
    }
251
252
    /**
253
     * @return string
254
     */
255
    public function getTitle()
256
    {
257
        return $this->JobTitle;
258
    }
259
260
    /**
261
     * @return FieldList
262
     */
263
    public function getCMSFields()
264
    {
265
        $fields = parent::getCMSFields();
266
        $fields->replaceField(
267
            'JobType',
268
            new DropdownField('JobType', $this->fieldLabel('JobType'), array(
269
                QueuedJob::IMMEDIATE => 'Immediate',
270
                QueuedJob::QUEUED => 'Queued',
271
                QueuedJob::LARGE => 'Large'
272
            ))
273
        );
274
        $statuses = array(
275
            QueuedJob::STATUS_NEW,
276
            QueuedJob::STATUS_INIT,
277
            QueuedJob::STATUS_RUN,
278
            QueuedJob::STATUS_WAIT,
279
            QueuedJob::STATUS_COMPLETE,
280
            QueuedJob::STATUS_PAUSED,
281
            QueuedJob::STATUS_CANCELLED,
282
            QueuedJob::STATUS_BROKEN
283
        );
284
        $fields->replaceField(
285
            'JobStatus',
286
            DropdownField::create('JobStatus', $this->fieldLabel('JobStatus'), array_combine($statuses, $statuses))
287
        );
288
289
        $fields->removeByName('SavedJobData');
290
        $fields->removeByName('SavedJobMessages');
291
292
        if (Permission::check('ADMIN')) {
293
            return $fields;
294
        } else {
295
            // Readonly CMS view is a lot more useful for debugging than no view at all
296
            return $fields->makeReadonly();
297
        }
298
    }
299
}
300