Jowens_JobQueue - Version 0.4.0

Version Notes

Update DJJob and fix timezone issues.

Download this release

Release Info

Developer Jordan Owens
Extension Jowens_JobQueue
Version 0.4.0
Comparing to
See all releases


Code changes from version 0.3.0 to 0.4.0

Files changed (31) hide show
  1. app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Job/View.php +78 -0
  2. app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Queue.php +15 -0
  3. app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Queue/Grid.php +178 -0
  4. app/code/community/Jowens/JobQueue.bak/Helper/Data.php +5 -0
  5. app/code/community/Jowens/JobQueue.bak/Model/.DS_Store +0 -0
  6. app/code/community/Jowens/JobQueue.bak/Model/Job.php +23 -0
  7. app/code/community/Jowens/JobQueue.bak/Model/Job/Abstract.php +62 -0
  8. app/code/community/Jowens/JobQueue.bak/Model/Order/Job.php +21 -0
  9. app/code/community/Jowens/JobQueue.bak/Model/Resource/Job.php +9 -0
  10. app/code/community/Jowens/JobQueue.bak/Model/Resource/Job/Collection.php +9 -0
  11. app/code/community/Jowens/JobQueue.bak/Model/Resource/Setup.php +5 -0
  12. app/code/community/Jowens/JobQueue.bak/Model/Worker.php +98 -0
  13. app/code/community/Jowens/JobQueue.bak/controllers/Adminhtml/QueueController.php +223 -0
  14. app/code/community/Jowens/JobQueue.bak/etc/adminhtml.xml +36 -0
  15. app/code/community/Jowens/JobQueue.bak/etc/config.xml +95 -0
  16. app/code/community/Jowens/JobQueue.bak/etc/system.xml +57 -0
  17. app/code/community/Jowens/{JobQueue/Model → JobQueue.bak/sql}/.DS_Store +0 -0
  18. app/code/community/Jowens/JobQueue.bak/sql/jobqueue_setup/mysql4-install-0.1.0.php +24 -0
  19. app/code/community/Jowens/JobQueue/Block/Adminhtml/Job/View.php +19 -16
  20. app/code/community/Jowens/JobQueue/Model/Job/Abstract.php +34 -35
  21. app/code/community/Jowens/JobQueue/Model/Worker.php +85 -82
  22. app/code/community/Jowens/JobQueue/controllers/Adminhtml/QueueController.php +25 -26
  23. app/code/community/Jowens/JobQueue/etc/config.xml +35 -35
  24. app/code/community/Jowens/JobQueue/etc/system.xml +1 -11
  25. lib/DJJob/.gitignore +1 -0
  26. lib/DJJob/DJJob.php +121 -39
  27. lib/DJJob/README.textile +2 -4
  28. lib/DJJob/test/custom_table_name.php +82 -0
  29. lib/DJJob/test/database.php +7 -4
  30. lib/DJJob/test/original_database_configure.php +79 -0
  31. package.xml +6 -6
app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Job/View.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Block_Adminhtml_Job_View extends Mage_Adminhtml_Block_Widget_Container
4
+ {
5
+
6
+ protected $_job;
7
+
8
+ public function __construct()
9
+ {
10
+ $this->_job = Mage::registry('jowens_jobqueue_job');
11
+
12
+ $this->_blockGroup = 'jobqueue';
13
+ $this->_controller = 'adminhtml_job';
14
+
15
+ parent::__construct();
16
+
17
+ $this->_addButton('back', array(
18
+ 'label' => Mage::helper('catalog')->__('Back'),
19
+ 'onclick' => 'setLocation(\''.$this->getUrl('*/*/', array('store'=>$this->getRequest()->getParam('store', 0))).'\')',
20
+ 'class' => 'back'
21
+ ), 0, -20);
22
+
23
+ $confirmMsg = $this->__('Are you sure you want to do this?');
24
+ $resubmitUrl = $this->getUrl('*/*/resubmit', array('id' => $this->_job->getId()));
25
+ $this->_addButton('resubmit', array(
26
+ 'label' => $this->__('Resubmit'),
27
+ 'onclick' => "confirmSetLocation('{$confirmMsg}', '{$resubmitUrl}')",
28
+ ), 0, -10);
29
+
30
+ if(!$this->_job->getFailedAt()) {
31
+ $cancelUrl = $this->getUrl('*/*/cancel', array('id' => $this->_job->getId()));
32
+ $this->_addButton('cancel', array(
33
+ 'label' => $this->__('Cancel'),
34
+ 'onclick' => "confirmSetLocation('{$confirmMsg}', '{$cancelUrl}')",
35
+ ), 0, -5);
36
+ }
37
+ }
38
+
39
+ public function getHeaderText()
40
+ {
41
+ return $this->__("Job: \"%s\"", $this->_job->getName());
42
+ }
43
+
44
+ protected function _toHtml()
45
+ {
46
+ $this->setJobIdHtml($this->escapeHtml($this->_job->getId()));
47
+ $this->setJobNameHtml($this->escapeHtml($this->_job->getName()));
48
+ $this->setJobNameHtml($this->escapeHtml($this->_job->getName()));
49
+
50
+ $storeId = $this->_job->getStoreId();
51
+ $store = Mage::app()->getStore($storeId);
52
+ $this->setStoreNameHtml($this->escapeHtml($store->getName()));
53
+
54
+ $this->setJobQueueHtml($this->escapeHtml($this->_job->getQueue()));
55
+ $this->setAttemptsHtml($this->escapeHtml($this->_job->getAttempts()));
56
+
57
+ $runAt = (strtotime($this->_job->getRunAt()))
58
+ ? $this->formatDate($this->_job->getRunAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
59
+ : $this->__('N/A');
60
+ $this->setRunAtHtml($this->escapeHtml($runAt));
61
+
62
+ $status = $this->__("Pending");
63
+ if( $this->_job->getFailedAt()) {
64
+ $status = $this->__('Failed');
65
+ } else if($this->_job->getLockedAt()) {
66
+ $status = $this->__('In Process');
67
+ }
68
+ $this->setStatusHtml($this->escapeHtml($status));
69
+
70
+ $this->setErrorHtml($this->escapeHtml($this->_job->getError()));
71
+
72
+ $createdAt = (strtotime($this->_job->getCreatedAt()))
73
+ ? $this->formatDate($this->_job->getCreatedAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
74
+ : $this->__('N/A');
75
+ $this->setCreatedAtHtml($this->escapeHtml($createdAt));
76
+ return parent::_toHtml();
77
+ }
78
+ }
app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Queue.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Block_Adminhtml_Queue extends Mage_Adminhtml_Block_Widget_Grid_Container
4
+ {
5
+ public function __construct()
6
+ {
7
+ $this->_blockGroup = 'jobqueue';
8
+ $this->_controller = 'adminhtml_queue';
9
+ $this->_headerText = $this->__('JobQueue');
10
+
11
+ parent::__construct();
12
+
13
+ $this->removeButton('add');
14
+ }
15
+ }
app/code/community/Jowens/JobQueue.bak/Block/Adminhtml/Queue/Grid.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Block_Adminhtml_Queue_Grid extends Mage_Adminhtml_Block_Widget_Grid
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+
9
+ $this->setDefaultSort('created_at');
10
+ $this->setId('jowens_jobqueue_grid');
11
+ $this->setDefaultDir('desc');
12
+ $this->setSaveParametersInSession(true);
13
+ }
14
+
15
+ protected function _getCollectionClass()
16
+ {
17
+ return 'jobqueue/job_collection';
18
+ }
19
+
20
+ protected function _prepareCollection()
21
+ {
22
+ $collection = Mage::getModel('jobqueue/job')->getCollection();
23
+ //$collection->getSelect()->columns('(`main_table`.`failed_at` is null) as status');
24
+ $collection->getSelect()->columns("(case when main_table.locked_at is not null then 2 when main_table.failed_at is null then 1 else 0 end) as status");
25
+ $this->setCollection($collection);
26
+
27
+ return parent::_prepareCollection();
28
+ }
29
+
30
+ protected function _addColumnFilterToCollection($column)
31
+ {
32
+ if ($column->getId() == 'status') {
33
+ $value = $column->getFilter()->getValue();
34
+ if($value == '2') {
35
+ $this->getCollection()->addFieldToFilter('locked_at', array('notnull'=> true));
36
+ } else {
37
+ $condition = $value == '1' ? 'null' : 'notnull';
38
+ $this->getCollection()->addFieldToFilter('failed_at', array($condition => true));
39
+ $this->getCollection()->addFieldToFilter('locked_at', array('null'=> true));
40
+ }
41
+ } else {
42
+ parent::_addColumnFilterToCollection($column);
43
+ }
44
+ return $this;
45
+ }
46
+
47
+
48
+ protected function _prepareColumns()
49
+ {
50
+ $this->addColumn('id',
51
+ array(
52
+ 'header'=> $this->__('ID'),
53
+ 'align' => 'right',
54
+ 'type' => 'number',
55
+ 'width' => '50px',
56
+ 'index' => 'id'
57
+ )
58
+ );
59
+
60
+ if (!Mage::app()->isSingleStoreMode()) {
61
+ $this->addColumn('store_id', array(
62
+ 'header' => $this->__('Store'),
63
+ 'index' => 'store_id',
64
+ 'type' => 'store',
65
+ 'store_view'=> true,
66
+ 'width' => '200px',
67
+ ));
68
+ }
69
+
70
+ $this->addColumn('name',
71
+ array(
72
+ 'header'=> $this->__('Name'),
73
+ 'index' => 'name'
74
+ )
75
+ );
76
+
77
+ $this->addColumn('queue',
78
+ array(
79
+ 'header'=> $this->__('Queue'),
80
+ 'index' => 'queue',
81
+ 'align' => 'center',
82
+ 'width' => '80px',
83
+ )
84
+ );
85
+
86
+ $this->addColumn('created_at',
87
+ array(
88
+ 'header'=> $this->__('Created At'),
89
+ 'index' => 'created_at',
90
+ 'type' => 'datetime',
91
+ 'width' => '175px',
92
+ 'align' => 'center',
93
+ )
94
+ );
95
+
96
+ $this->addColumn('run_at',
97
+ array(
98
+ 'header'=> $this->__('Run At'),
99
+ 'index' => 'run_at',
100
+ 'type' => 'datetime',
101
+ 'align' => 'center',
102
+ )
103
+ );
104
+
105
+ $this->addColumn('attempts',
106
+ array(
107
+ 'header'=> $this->__('Attempts'),
108
+ 'index' => 'attempts',
109
+ 'type' => 'number',
110
+ 'align' => 'center',
111
+ 'width' => '100px',
112
+ )
113
+ );
114
+
115
+ $this->addColumn('status',
116
+ array(
117
+ 'header'=> $this->__('Status'),
118
+ 'index' => 'status',
119
+ 'type' => 'options',
120
+ 'options' => array('1'=>'Pending', '2'=>'In Process', '0'=>'Failed'),
121
+ 'align' => 'center',
122
+ 'width' => '80px',
123
+ )
124
+ );
125
+
126
+ $this->addColumn('action',
127
+ array(
128
+ 'header' => $this->__('Action'),
129
+ 'width' => '50px',
130
+ 'type' => 'action',
131
+ 'getter' => 'getId',
132
+ 'actions' => array(
133
+ array(
134
+ 'caption' => $this->__('View'),
135
+ 'url' => array('base'=>'*/*/view'),
136
+ 'field' => 'id'
137
+ )
138
+ ),
139
+ 'filter' => false,
140
+ 'sortable' => false,
141
+ 'align' => 'center',
142
+ )
143
+ );
144
+
145
+ return parent::_prepareColumns();
146
+ }
147
+
148
+ protected function _prepareMassaction()
149
+ {
150
+ $this->setMassactionIdField('id');
151
+ $this->getMassactionBlock()->setFormFieldName('job_id');
152
+
153
+ $this->getMassactionBlock()->addItem('resubmit_job', array(
154
+ 'label' => $this->__('Resubmit Job'),
155
+ 'url' => $this->getUrl('*/*/massResubmitJob'),
156
+ 'confirm' => $this->__('Are you sure?')
157
+ ));
158
+
159
+ $this->getMassactionBlock()->addItem('cancel_job', array(
160
+ 'label' => $this->__('Cancel Job'),
161
+ 'url' => $this->getUrl('*/*/massCancelJob'),
162
+ 'confirm' => $this->__('Are you sure?')
163
+ ));
164
+
165
+ $this->getMassactionBlock()->addItem('delete_job', array(
166
+ 'label' => $this->__('Delete Job'),
167
+ 'url' => $this->getUrl('*/*/massDeleteJob'),
168
+ 'confirm' => $this->__('Are you sure?')
169
+ ));
170
+
171
+ return $this;
172
+ }
173
+
174
+ public function getRowUrl($row)
175
+ {
176
+ return $this->getUrl('*/*/view', array('id' => $row->getId()));
177
+ }
178
+ }
app/code/community/Jowens/JobQueue.bak/Helper/Data.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ }
app/code/community/Jowens/JobQueue.bak/Model/.DS_Store ADDED
Binary file
app/code/community/Jowens/JobQueue.bak/Model/Job.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Model_Job extends Mage_Core_Model_Abstract
4
+ {
5
+ protected function _construct()
6
+ {
7
+ $this->_init('jobqueue/job');
8
+ }
9
+
10
+ public function resubmit() {
11
+ $this->setFailedAt(null);
12
+ $this->setRunAt(null);
13
+ $this->setAttempts(0);
14
+ $this->setError(null);
15
+ $this->save();
16
+ }
17
+
18
+ public function cancel() {
19
+ $this->setFailedAt(Mage::getModel('core/date')->timestamp(time()));
20
+ $this->setError(Mage::helper('jobqueue')->__("Job canceled"));
21
+ $this->save();
22
+ }
23
+ }
app/code/community/Jowens/JobQueue.bak/Model/Job/Abstract.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Jowens_JobQueue_Model_Job_Abstract extends Mage_Core_Model_Abstract
4
+ {
5
+ private $name;
6
+ private $storeId;
7
+
8
+ public function __construct($name=null) {
9
+ $this->name = $name ? $name : $this->getType();
10
+
11
+ $this->setStoreId(Mage::app()->getStore()->getStoreId());
12
+ }
13
+
14
+ public abstract function perform();
15
+
16
+ public function performImmediate($retryQueue="default") {
17
+ try {
18
+ $this->perform();
19
+ } catch(Exception $e) {
20
+ $this->enqueue($retryQueue);
21
+ Mage::log($e);
22
+ }
23
+ }
24
+
25
+ public function enqueue($queue="default", $run_at=null) {
26
+ $job = Mage::getModel('jobqueue/job');
27
+ $job->setStoreId($this->getStoreId());
28
+ $job->setName($this->getName());
29
+ $job->setHandler(serialize($this));
30
+ $job->setQueue($queue);
31
+ $job->setRunAt($run_at);
32
+ $job->setCreatedAt(now());
33
+ $job->save();
34
+ }
35
+
36
+ public function setName($name)
37
+ {
38
+ $this->name = $name;
39
+ return $this;
40
+ }
41
+
42
+ public function getName()
43
+ {
44
+ return $this->name;
45
+ }
46
+
47
+ public function setStoreId($storeId)
48
+ {
49
+ $this->storeId = $storeId;
50
+ return $this;
51
+ }
52
+
53
+ public function getStoreId()
54
+ {
55
+ return $this->storeId;
56
+ }
57
+
58
+ public function getType()
59
+ {
60
+ return get_class($this);
61
+ }
62
+ }
app/code/community/Jowens/JobQueue.bak/Model/Order/Job.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Model_Order_Job extends Jowens_JobQueue_Model_Job_Abstract
4
+ {
5
+ private $productData;
6
+
7
+ public function perform() {
8
+ Mage::log("Hello world!");
9
+ Mage::log($this->getProductData());
10
+ //Mage::log("DATA: " . implode(unserialize(base64_decode($this->getProductData()))));
11
+
12
+ }
13
+
14
+ public function setProductData($data) {
15
+ $this->productData = $data;
16
+ }
17
+
18
+ public function getProductData() {
19
+ return $this->productData;
20
+ }
21
+ }
app/code/community/Jowens/JobQueue.bak/Model/Resource/Job.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Model_Resource_Job extends Mage_Core_Model_Resource_Db_Abstract
4
+ {
5
+ protected function _construct()
6
+ {
7
+ $this->_init('jobqueue/job', 'id');
8
+ }
9
+ }
app/code/community/Jowens/JobQueue.bak/Model/Resource/Job/Collection.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Model_Resource_Job_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
4
+ {
5
+ protected function _construct()
6
+ {
7
+ $this->_init('jobqueue/job');
8
+ }
9
+ }
app/code/community/Jowens/JobQueue.bak/Model/Resource/Setup.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
4
+ {
5
+ }
app/code/community/Jowens/JobQueue.bak/Model/Worker.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ set_include_path(get_include_path().PS.Mage::getBaseDir('lib').DS.'DJJob');
4
+
5
+ require_once('DJJob.php');
6
+
7
+ class Jowens_JobQueue_Model_Worker extends Mage_Core_Model_Abstract
8
+ {
9
+ const DEFAULT_QUEUE = 'default';
10
+
11
+ private $workerName;
12
+ private $queue;
13
+
14
+ public function __construct() {
15
+ list($hostname, $pid) = array(trim(`hostname`), getmypid());
16
+ $this->workerName = "host::$hostname pid::$pid";
17
+ $this->queue = Mage::getStoreConfig('jobqueue/config/queue');
18
+ if(empty($this->queue)) {
19
+ $this->queue = self::DEFAULT_QUEUE;
20
+ }
21
+ }
22
+
23
+ public function getQueue() {
24
+ return $this->queue;
25
+ }
26
+
27
+ public function setQueue($queue) {
28
+ $this->queue = $queue;
29
+ }
30
+
31
+ public function getWorkerName() {
32
+ return $this->workerName;
33
+ }
34
+
35
+
36
+ public function executeJobs($schedule=null) {
37
+ if(!Mage::getStoreConfig('jobqueue/config/enabled')) {
38
+ return;
39
+ }
40
+
41
+ if($schedule) {
42
+ $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
43
+ $jobConfig = $jobsRoot->{$schedule->getJobCode()};
44
+ $queue = $jobConfig->queue;
45
+ if($queue) {
46
+ $this->setQueue($queue);
47
+ }
48
+ }
49
+
50
+ $this->setupDJJob();
51
+
52
+ try {
53
+ $collection = Mage::getModel('jobqueue/job')->getCollection();
54
+ $collection->addFieldToFilter('queue', array('eq' => $this->getQueue()))
55
+ ->addFieldToFilter('run_at', array(
56
+ array('null' => true),
57
+ array('lteq'=> date('Y-m-d H:i:s', Mage::app()->getLocale()->storeTimeStamp()))
58
+ ))
59
+ ->addFieldToFilter(array('locked_at', 'locked_by'), array(
60
+ array('locked_at', 'null' => true),
61
+ array('locked_by', 'eq' => $this->workerName)
62
+ ))
63
+ ->addFieldToFilter('failed_at', array('null' => true))
64
+ ->addFieldToFilter('attempts', array('lt' => (int)Mage::getStoreConfig('jobqueue/config/max_attempts')));
65
+
66
+ // randomly order to prevent lock contention among workers
67
+ $collection->getSelect()->order(new Zend_Db_Expr('RAND()'));
68
+ $collection->load();
69
+
70
+ foreach($collection as $row) {
71
+ $job = new DJJob($this->workerName, $row->getId(), array(
72
+ "max_attempts" => Mage::getStoreConfig('jobqueue/config/max_attempts')
73
+ ));
74
+ if ($job->acquireLock()) {
75
+ $job->run();
76
+ }
77
+ }
78
+ } catch (Exception $e) {
79
+ Mage::log($e);
80
+ }
81
+ }
82
+
83
+ protected function setupDJJob() {
84
+ $config = Mage::getConfig()->getResourceConnectionConfig("default_setup");
85
+
86
+ $dsn = "";
87
+ if (strpos($config->host, '/') !== false) {
88
+ $dsn = "mysql:unix_socket=" . $config->host . ";dbname=" . $config->dbname;
89
+ } else {
90
+ $dsn = "mysql:host=" . $config->host . ";dbname=" . $config->dbname . ";port=" . $config->port;
91
+ }
92
+
93
+ DJJob::configure(
94
+ $dsn,
95
+ array('mysql_user' => $config->username, 'mysql_pass' => $config->password)
96
+ );
97
+ }
98
+ }
app/code/community/Jowens/JobQueue.bak/controllers/Adminhtml/QueueController.php ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ public function indexAction()
6
+ {
7
+ $worker = Mage::getModel('jobqueue/worker');
8
+ $worker->executeJobs();
9
+
10
+ //$data = base64_encode(serialize(array("name" => "Jacket''s, Size: M", "color" => "blue")));
11
+ $data = array("name" => 'Jacket:;, Size: M', "color" => "blue");
12
+ $job = Mage::getModel('jobqueue/order_job');
13
+ $job->setProductData($data);
14
+ $job->setName('Order# 12345')
15
+ ->enqueue();
16
+
17
+ $this->_init()
18
+ ->renderLayout();
19
+ }
20
+
21
+ protected function _init()
22
+ {
23
+ $this->loadLayout()
24
+ ->_setActiveMenu('system/jowens_jobqueue_queue')
25
+ ->_title($this->__('System'))->_title($this->__('JobQueue'))
26
+ ->_addBreadcrumb($this->__('System'), $this->__('System'))
27
+ ->_addBreadcrumb($this->__('JobQueue'), $this->__('JobQueue'));
28
+
29
+ return $this;
30
+ }
31
+
32
+ public function viewAction()
33
+ {
34
+ $id = $this->getRequest()->getParam('id');
35
+ $job = Mage::getModel('jobqueue/job');
36
+
37
+ if ($id) {
38
+ $job->load($id);
39
+
40
+ if (!$job->getId()) {
41
+ Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
42
+ $this->_redirect('*/*/index');
43
+ return;
44
+ }
45
+ }
46
+
47
+ $this->_title($job->getId() ? $job->getName() : "Job Details");
48
+
49
+ $data = Mage::getSingleton('adminhtml/session')->getJobData(true);
50
+ if (!empty($data)) {
51
+ $job->setData($data);
52
+ }
53
+
54
+ Mage::register('jowens_jobqueue_job', $job);
55
+
56
+ $this->_init()
57
+ ->renderLayout();
58
+ }
59
+
60
+ public function resubmitAction()
61
+ {
62
+ $id = $this->getRequest()->getParam('id');
63
+ $job = Mage::getModel('jobqueue/job');
64
+
65
+ if ($id) {
66
+ $job->load($id);
67
+
68
+ if (!$job->getId()) {
69
+ Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
70
+ $this->_redirect('*/*/index');
71
+ return;
72
+ }
73
+
74
+ try {
75
+ $job->resubmit();
76
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Job "%s" has been resubmitted', $job->getName()));
77
+ } catch (Exception $e) {
78
+ Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be resubmitted', $job->getName()));
79
+ }
80
+ }
81
+ $this->_redirect('*/*/index');
82
+ }
83
+
84
+ public function cancelAction()
85
+ {
86
+ $id = $this->getRequest()->getParam('id');
87
+ $job = Mage::getModel('jobqueue/job');
88
+
89
+ if ($id) {
90
+ $job->load($id);
91
+
92
+ if (!$job->getId()) {
93
+ Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
94
+ $this->_redirect('*/*/index');
95
+ return;
96
+ }
97
+
98
+ try {
99
+ $job->cancel();
100
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Job "%s" has been canceled', $job->getName()));
101
+ } catch (Exception $e) {
102
+ Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be canceled', $job->getName()));
103
+ }
104
+ }
105
+ $this->_redirect('*/*/index');
106
+ }
107
+
108
+ public function deleteAction()
109
+ {
110
+ $id = $this->getRequest()->getParam('id');
111
+ $job = Mage::getModel('jobqueue/job');
112
+
113
+ if ($id) {
114
+ $job->load($id);
115
+
116
+ if (!$job->getId()) {
117
+ Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
118
+ $this->_redirect('*/*/index');
119
+ return;
120
+ }
121
+
122
+ try {
123
+ $job->delete();
124
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Job "%s" has been deleted', $job->getName()));
125
+ } catch (Exception $e) {
126
+ Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be deleted', $job->getName()));
127
+ }
128
+ }
129
+ $this->_redirect('*/*/index');
130
+ }
131
+
132
+ public function massResubmitJobAction()
133
+ {
134
+ $jobIds = $this->getRequest()->getParam('job_id');
135
+ $success = 0;
136
+ $error = 0;
137
+
138
+ foreach($jobIds as $jobId) {
139
+ $job = Mage::getModel('jobqueue/job')->load($jobId);
140
+ try {
141
+ $job->resubmit();
142
+ $success++;
143
+ } catch (Exception $e) {
144
+ Mage::log($e);
145
+ $error++;
146
+ }
147
+ }
148
+
149
+
150
+ if($error) {
151
+ Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be resubmitted', $error));
152
+ }
153
+
154
+ if($success) {
155
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) resubmitted', $success));
156
+ }
157
+
158
+ $this->_redirect('*/*/index');
159
+ }
160
+
161
+ public function massCancelJobAction()
162
+ {
163
+ $jobIds = $this->getRequest()->getParam('job_id');
164
+ $success = 0;
165
+ $error = 0;
166
+
167
+ foreach($jobIds as $jobId) {
168
+ $job = Mage::getModel('jobqueue/job')->load($jobId);
169
+ try {
170
+ if($job->getFailedAt()) {
171
+ $error++;
172
+ } else {
173
+ $job->cancel();
174
+ $success++;
175
+ }
176
+ } catch (Exception $e) {
177
+ Mage::log($e);
178
+ $error++;
179
+ }
180
+ }
181
+
182
+
183
+ if($error) {
184
+ Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be canceled', $error));
185
+ }
186
+
187
+ if($success) {
188
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) canceled', $success));
189
+ }
190
+
191
+
192
+ $this->_redirect('*/*/index');
193
+ }
194
+
195
+ public function massDeleteJobAction()
196
+ {
197
+ $jobIds = $this->getRequest()->getParam('job_id');
198
+ $success = 0;
199
+ $error = 0;
200
+
201
+ foreach($jobIds as $jobId) {
202
+ $job = Mage::getModel('jobqueue/job')->load($jobId);
203
+ try {
204
+ $job->delete();
205
+ $success++;
206
+ } catch (Exception $e) {
207
+ Mage::log($e);
208
+ $error++;
209
+ }
210
+ }
211
+
212
+
213
+ if($error) {
214
+ Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be deleted', $error));
215
+ }
216
+
217
+ if($success) {
218
+ Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) deleted', $success));
219
+ }
220
+
221
+ $this->_redirect('*/*/index');
222
+ }
223
+ }
app/code/community/Jowens/JobQueue.bak/etc/adminhtml.xml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <menu>
4
+ <system>
5
+ <children>
6
+ <jowens_jobqueue_queue translate="title" module="jobqueue">
7
+ <title>JobQueue</title>
8
+ <action>adminhtml/queue</action>
9
+ </jowens_jobqueue_queue>
10
+ </children>
11
+ </system>
12
+ </menu>
13
+
14
+ <acl>
15
+ <resources>
16
+ <admin>
17
+ <children>
18
+ <system>
19
+ <children>
20
+ <jowens_jobqueue_queue>
21
+ <title>JobQueue</title>
22
+ </jowens_jobqueue_queue>
23
+ <config>
24
+ <children>
25
+ <jobqueue translate="title" module="jobqueue">
26
+ <title>JobQueue Configuration</title>
27
+ </jobqueue>
28
+ </children>
29
+ </config>
30
+ </children>
31
+ </system>
32
+ </children>
33
+ </admin>
34
+ </resources>
35
+ </acl>
36
+ </config>
app/code/community/Jowens/JobQueue.bak/etc/config.xml ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <config>
3
+ <modules>
4
+ <Jowens_JobQueue>
5
+ <version>0.2.0</version>
6
+ </Jowens_JobQueue>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <jobqueue>
11
+ <class>Jowens_JobQueue_Model</class>
12
+ <resourceModel>jobqueue_resource</resourceModel>
13
+ </jobqueue>
14
+ <jobqueue_resource>
15
+ <class>Jowens_JobQueue_Model_Resource</class>
16
+ <entities>
17
+ <job>
18
+ <table>jobs</table>
19
+ </job>
20
+ </entities>
21
+ </jobqueue_resource>
22
+ </models>
23
+ <blocks>
24
+ <jobqueue>
25
+ <class>Jowens_JobQueue_Block</class>
26
+ </jobqueue>
27
+ </blocks>
28
+ <helpers>
29
+ <jobqueue>
30
+ <class>Jowens_JobQueue_Helper</class>
31
+ </jobqueue>
32
+ </helpers>
33
+ <resources>
34
+ <jobqueue_setup>
35
+ <setup>
36
+ <module>Jowens_JobQueue</module>
37
+ <class>Jowens_JobQueue_Model_Resource_Setup</class>
38
+ </setup>
39
+ </jobqueue_setup>
40
+ </resources>
41
+ </global>
42
+ <frontend>
43
+ <routers>
44
+ <jowens_jobqueue>
45
+ <use>standard</use>
46
+ <args>
47
+ <module>Jowens_JobQueue</module>
48
+ <frontName>jobqueue</frontName>
49
+ </args>
50
+ </jowens_jobqueue>
51
+ </routers>
52
+ </frontend>
53
+ <admin>
54
+ <routers>
55
+ <adminhtml>
56
+ <args>
57
+ <modules>
58
+ <Jowens_JobQueue before="Mage_Adminhtml">Jowens_JobQueue_Adminhtml</Jowens_JobQueue>
59
+ </modules>
60
+ </args>
61
+ </adminhtml>
62
+ </routers>
63
+ </admin>
64
+ <adminhtml>
65
+ <layout>
66
+ <updates>
67
+ <jowens_jobqueue>
68
+ <file>jowens/jobqueue.xml</file>
69
+ </jowens_jobqueue>
70
+ </updates>
71
+ </layout>
72
+ </adminhtml>
73
+ <crontab>
74
+ <jobs>
75
+ <jobqueue_default>
76
+ <schedule>
77
+ <config_path>jobqueue/config/cron_expr</config_path>
78
+ </schedule>
79
+ <run>
80
+ <model>jobqueue/worker::executeJobs</model>
81
+ </run>
82
+ </jobqueue_default>
83
+ </jobs>
84
+ </crontab>
85
+ <default>
86
+ <jobqueue>
87
+ <config>
88
+ <enabled>1</enabled>
89
+ <cron_expr>*/5 * * * *</cron_expr>
90
+ <max_attempts>10</max_attempts>
91
+ <queue>default</queue>
92
+ </config>
93
+ </jobqueue>
94
+ </default>
95
+ </config>
app/code/community/Jowens/JobQueue.bak/etc/system.xml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <jobqueue translate="label" module="jobqueue">
5
+ <label>JobQueue</label>
6
+ <tab>general</tab>
7
+ <frontend_type>text</frontend_type>
8
+ <sort_order>10000</sort_order>
9
+ <show_in_default>1</show_in_default>
10
+ <groups>
11
+ <config translate="label" module="jobqueue">
12
+ <label>Configuration</label>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>2</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <fields>
19
+ <enabled translate="label comment">
20
+ <label>Cron Worker Enabled</label>
21
+ <frontend_type>select</frontend_type>
22
+ <source_model>adminhtml/system_config_source_yesno</source_model>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>0</show_in_website>
25
+ <show_in_store>0</show_in_store>
26
+ </enabled>
27
+ <cron_expr>
28
+ <label>How often do you want the cron to run?</label>
29
+ <frontend_type>text</frontend_type>
30
+ <sort_order>40</sort_order>
31
+ <comment>Use Crontab Format (Eg. "*/5 * * * *" for every 5 minutes)</comment>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>0</show_in_website>
34
+ <show_in_store>0</show_in_store>
35
+ </cron_expr>
36
+ <max_attempts>
37
+ <label>Max Attempts</label>
38
+ <frontend_type>text</frontend_type>
39
+ <sort_order>50</sort_order>
40
+ <show_in_default>1</show_in_default>
41
+ <show_in_website>0</show_in_website>
42
+ <show_in_store>0</show_in_store>
43
+ </max_attempts>
44
+ <queue>
45
+ <label>Queue</label>
46
+ <frontend_type>text</frontend_type>
47
+ <sort_order>60</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>0</show_in_website>
50
+ <show_in_store>0</show_in_store>
51
+ </queue>
52
+ </fields>
53
+ </config>
54
+ </groups>
55
+ </jobqueue>
56
+ </sections>
57
+ </config>
app/code/community/Jowens/{JobQueue/Model → JobQueue.bak/sql}/.DS_Store RENAMED
File without changes
app/code/community/Jowens/JobQueue.bak/sql/jobqueue_setup/mysql4-install-0.1.0.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+
5
+ $installer->startSetup();
6
+
7
+ $installer->run(
8
+ "CREATE TABLE " . $installer->getTable('jobqueue/job')." (
9
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
10
+ `store_id` INT UNSIGNED NOT NULL DEFAULT 0,
11
+ `name` VARCHAR(255),
12
+ `handler` TEXT NOT NULL,
13
+ `queue` VARCHAR(255) NOT NULL DEFAULT 'default',
14
+ `attempts` INT UNSIGNED NOT NULL DEFAULT 0,
15
+ `run_at` DATETIME NULL,
16
+ `locked_at` DATETIME NULL,
17
+ `locked_by` VARCHAR(255) NULL,
18
+ `failed_at` DATETIME NULL,
19
+ `error` TEXT NULL,
20
+ `created_at` DATETIME NOT NULL
21
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
22
+ );
23
+
24
+ $installer->endSetup();
app/code/community/Jowens/JobQueue/Block/Adminhtml/Job/View.php CHANGED
@@ -6,16 +6,19 @@ class Jowens_JobQueue_Block_Adminhtml_Job_View extends Mage_Adminhtml_Block_Widg
6
  protected $_job;
7
 
8
  public function __construct()
9
- {
10
  $this->_job = Mage::registry('jowens_jobqueue_job');
11
 
12
  $this->_blockGroup = 'jobqueue';
13
  $this->_controller = 'adminhtml_job';
14
-
15
  parent::__construct();
16
- $this->_removeButton('save');
17
- $this->_removeButton('reset');
18
-
 
 
 
19
  $confirmMsg = $this->__('Are you sure you want to do this?');
20
  $resubmitUrl = $this->getUrl('*/*/resubmit', array('id' => $this->_job->getId()));
21
  $this->_addButton('resubmit', array(
@@ -28,14 +31,14 @@ class Jowens_JobQueue_Block_Adminhtml_Job_View extends Mage_Adminhtml_Block_Widg
28
  $this->_addButton('cancel', array(
29
  'label' => $this->__('Cancel'),
30
  'onclick' => "confirmSetLocation('{$confirmMsg}', '{$cancelUrl}')",
31
- ), 0, -5);
32
- }
33
- }
34
-
35
  public function getHeaderText()
36
- {
37
- return $this->__("Job: \"%s\"", $this->_job->getName());
38
- }
39
 
40
  protected function _toHtml()
41
  {
@@ -53,7 +56,7 @@ class Jowens_JobQueue_Block_Adminhtml_Job_View extends Mage_Adminhtml_Block_Widg
53
  $runAt = (strtotime($this->_job->getRunAt()))
54
  ? $this->formatDate($this->_job->getRunAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
55
  : $this->__('N/A');
56
- $this->setRunAtHtml($this->escapeHtml($runAt));
57
 
58
  $status = $this->__("Pending");
59
  if( $this->_job->getFailedAt()) {
@@ -62,13 +65,13 @@ class Jowens_JobQueue_Block_Adminhtml_Job_View extends Mage_Adminhtml_Block_Widg
62
  $status = $this->__('In Process');
63
  }
64
  $this->setStatusHtml($this->escapeHtml($status));
65
-
66
  $this->setErrorHtml($this->escapeHtml($this->_job->getError()));
67
 
68
  $createdAt = (strtotime($this->_job->getCreatedAt()))
69
  ? $this->formatDate($this->_job->getCreatedAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
70
  : $this->__('N/A');
71
- $this->setCreatedAtHtml($this->escapeHtml($createdAt));
72
  return parent::_toHtml();
73
- }
74
  }
6
  protected $_job;
7
 
8
  public function __construct()
9
+ {
10
  $this->_job = Mage::registry('jowens_jobqueue_job');
11
 
12
  $this->_blockGroup = 'jobqueue';
13
  $this->_controller = 'adminhtml_job';
14
+
15
  parent::__construct();
16
+
17
+ $this->_addButton('back', array(
18
+ 'label' => Mage::helper('catalog')->__('Back'),
19
+ 'onclick' => 'setLocation(\''.$this->getUrl('*/*/', array('store'=>$this->getRequest()->getParam('store', 0))).'\')',
20
+ 'class' => 'back'
21
+ ), 0, -20);
22
  $confirmMsg = $this->__('Are you sure you want to do this?');
23
  $resubmitUrl = $this->getUrl('*/*/resubmit', array('id' => $this->_job->getId()));
24
  $this->_addButton('resubmit', array(
31
  $this->_addButton('cancel', array(
32
  'label' => $this->__('Cancel'),
33
  'onclick' => "confirmSetLocation('{$confirmMsg}', '{$cancelUrl}')",
34
+ ), 0, -5);
35
+ }
36
+ }
37
+
38
  public function getHeaderText()
39
+ {
40
+ return $this->__("Job: \"%s\"", $this->_job->getName());
41
+ }
42
 
43
  protected function _toHtml()
44
  {
56
  $runAt = (strtotime($this->_job->getRunAt()))
57
  ? $this->formatDate($this->_job->getRunAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
58
  : $this->__('N/A');
59
+ $this->setRunAtHtml($this->escapeHtml($runAt));
60
 
61
  $status = $this->__("Pending");
62
  if( $this->_job->getFailedAt()) {
65
  $status = $this->__('In Process');
66
  }
67
  $this->setStatusHtml($this->escapeHtml($status));
68
+
69
  $this->setErrorHtml($this->escapeHtml($this->_job->getError()));
70
 
71
  $createdAt = (strtotime($this->_job->getCreatedAt()))
72
  ? $this->formatDate($this->_job->getCreatedAt(), Mage_Core_Model_Locale::FORMAT_TYPE_MEDIUM, true)
73
  : $this->__('N/A');
74
+ $this->setCreatedAtHtml($this->escapeHtml($createdAt));
75
  return parent::_toHtml();
76
+ }
77
  }
app/code/community/Jowens/JobQueue/Model/Job/Abstract.php CHANGED
@@ -2,23 +2,22 @@
2
 
3
  abstract class Jowens_JobQueue_Model_Job_Abstract extends Mage_Core_Model_Abstract
4
  {
5
- private $name;
6
- private $storeId;
7
 
8
- public function __construct($name=null) {
9
- $this->name = $name ? $name : $this->getType();
10
-
11
- $this->setStoreId(Mage::app()->getStore()->getStoreId());
12
- }
13
 
14
- public abstract function perform();
15
 
16
  public function performImmediate($retryQueue="default") {
17
  try {
18
  $this->perform();
19
  } catch(Exception $e) {
20
  $this->enqueue($retryQueue);
21
- Mage::log($e);
22
  }
23
  }
24
 
@@ -33,30 +32,30 @@ abstract class Jowens_JobQueue_Model_Job_Abstract extends Mage_Core_Model_Abstra
33
  $job->save();
34
  }
35
 
36
- public function setName($name)
37
- {
38
- $this->name = $name;
39
- return $this;
40
- }
41
-
42
- public function getName()
43
- {
44
- return $this->name;
45
- }
46
-
47
- public function setStoreId($storeId)
48
- {
49
- $this->storeId = $storeId;
50
- return $this;
51
- }
52
-
53
- public function getStoreId()
54
- {
55
- return $this->storeId;
56
- }
57
-
58
- public function getType()
59
- {
60
- return get_class($this);
61
- }
62
  }
2
 
3
  abstract class Jowens_JobQueue_Model_Job_Abstract extends Mage_Core_Model_Abstract
4
  {
5
+ private $name;
6
+ private $storeId;
7
 
8
+ public function __construct($name=null) {
9
+ $this->name = $name ? $name : $this->getType();
10
+ $this->setStoreId(Mage::app()->getStore()->getStoreId());
11
+ }
 
12
 
13
+ public abstract function perform();
14
 
15
  public function performImmediate($retryQueue="default") {
16
  try {
17
  $this->perform();
18
  } catch(Exception $e) {
19
  $this->enqueue($retryQueue);
20
+ Mage::logException($e);
21
  }
22
  }
23
 
32
  $job->save();
33
  }
34
 
35
+ public function setName($name)
36
+ {
37
+ $this->name = $name;
38
+ return $this;
39
+ }
40
+
41
+ public function getName()
42
+ {
43
+ return $this->name;
44
+ }
45
+
46
+ public function setStoreId($storeId)
47
+ {
48
+ $this->storeId = $storeId;
49
+ return $this;
50
+ }
51
+
52
+ public function getStoreId()
53
+ {
54
+ return $this->storeId;
55
+ }
56
+
57
+ public function getType()
58
+ {
59
+ return get_class($this);
60
+ }
61
  }
app/code/community/Jowens/JobQueue/Model/Worker.php CHANGED
@@ -6,93 +6,96 @@ require_once('DJJob.php');
6
 
7
  class Jowens_JobQueue_Model_Worker extends Mage_Core_Model_Abstract
8
  {
9
- const DEFAULT_QUEUE = 'default';
10
 
11
- private $workerName;
12
- private $queue;
13
-
14
- public function __construct() {
15
- list($hostname, $pid) = array(trim(`hostname`), getmypid());
16
  $this->workerName = "host::$hostname pid::$pid";
17
  $this->queue = Mage::getStoreConfig('jobqueue/config/queue');
18
  if(empty($this->queue)) {
19
- $this->queue = self::DEFAULT_QUEUE;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
- }
22
-
23
- public function getQueue() {
24
- return $this->queue;
25
- }
26
-
27
- public function setQueue($queue) {
28
- $this->queue = $queue;
29
- }
30
-
31
- public function getWorkerName() {
32
- return $this->workerName;
33
- }
34
-
35
-
36
- public function executeJobs($schedule=null) {
37
- if(!Mage::getStoreConfig('jobqueue/config/enabled')) {
38
- return;
39
- }
40
-
41
- if($schedule) {
42
- $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
43
- $jobConfig = $jobsRoot->{$schedule->getJobCode()};
44
- $queue = $jobConfig->queue;
45
- if($queue) {
46
- $this->setQueue($queue);
47
- }
48
- }
49
-
50
- $this->setupDJJob();
51
-
52
- try {
53
- $collection = Mage::getModel('jobqueue/job')->getCollection();
54
- $collection->addFieldToFilter('queue', array('eq' => $this->getQueue()))
55
- ->addFieldToFilter('run_at', array(
56
- array('null' => true),
57
- array('lteq'=> date('Y-m-d H:i:s', Mage::app()->getLocale()->storeTimeStamp()))
58
- ))
59
- ->addFieldToFilter(array('locked_at', 'locked_by'), array(
60
- array('locked_at', 'null' => true),
61
- array('locked_by', 'eq' => $this->workerName)
62
- ))
63
- ->addFieldToFilter('failed_at', array('null' => true))
64
- ->addFieldToFilter('attempts', array('lt' => (int)Mage::getStoreConfig('jobqueue/config/max_attempts')));
65
-
66
- // randomly order to prevent lock contention among workers
67
- $collection->getSelect()->order(new Zend_Db_Expr('RAND()'));
68
- $collection->load();
69
-
70
- foreach($collection as $row) {
71
- $job = new DJJob($this->workerName, $row->getId(), array(
72
- "max_attempts" => Mage::getStoreConfig('jobqueue/config/max_attempts')
73
- ));
74
- if ($job->acquireLock()) {
75
- $job->run();
76
- }
77
- }
78
- } catch (Exception $e) {
79
- Mage::log($e);
80
- }
81
- }
82
 
83
- protected function setupDJJob() {
84
- $config = Mage::getConfig()->getResourceConnectionConfig("default_setup");
85
-
86
- $dsn = "";
87
- if (strpos($config->host, '/') !== false) {
88
- $dsn = "mysql:unix_socket=" . $config->host . ";dbname=" . $config->dbname;
89
- } else {
90
- $dsn = "mysql:host=" . $config->host . ";dbname=" . $config->dbname . ";port=" . $config->port;
91
- }
92
 
93
- DJJob::configure(
94
- $dsn,
95
- array('mysql_user' => $config->username, 'mysql_pass' => $config->password)
96
- );
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
6
 
7
  class Jowens_JobQueue_Model_Worker extends Mage_Core_Model_Abstract
8
  {
9
+ const DEFAULT_QUEUE = 'default';
10
 
11
+ private $workerName;
12
+ private $queue;
13
+
14
+ public function __construct() {
15
+ list($hostname, $pid) = array(trim(`hostname`), getmypid());
16
  $this->workerName = "host::$hostname pid::$pid";
17
  $this->queue = Mage::getStoreConfig('jobqueue/config/queue');
18
  if(empty($this->queue)) {
19
+ $this->queue = self::DEFAULT_QUEUE;
20
+ }
21
+ }
22
+
23
+ public function getQueue() {
24
+ return $this->queue;
25
+ }
26
+
27
+ public function setQueue($queue) {
28
+ $this->queue = $queue;
29
+ }
30
+
31
+ public function getWorkerName() {
32
+ return $this->workerName;
33
+ }
34
+
35
+ public function executeJobs($schedule=null) {
36
+ if(!Mage::getStoreConfig('jobqueue/config/enabled')) {
37
+ return;
38
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ if($schedule) {
41
+ $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
42
+ $jobConfig = $jobsRoot->{$schedule->getJobCode()};
43
+ $queue = $jobConfig->queue;
44
+ if($queue) {
45
+ $this->setQueue($queue);
46
+ }
47
+ }
 
48
 
49
+ $this->setupDJJob();
50
+
51
+ try {
52
+ $collection = Mage::getModel('jobqueue/job')->getCollection();
53
+ $collection->addFieldToFilter('queue', array('eq' => $this->getQueue()))
54
+ ->addFieldToFilter('run_at', array(
55
+ array('null' => true),
56
+ array('lteq' => now())
57
+ ))
58
+ ->addFieldToFilter(array('locked_at', 'locked_by'), array(
59
+ array('locked_at', 'null' => true),
60
+ array('locked_by', 'eq' => $this->workerName)
61
+ ))
62
+ ->addFieldToFilter('failed_at', array('null' => true))
63
+ ->addFieldToFilter('attempts', array('lt' => (int)Mage::getStoreConfig('jobqueue/config/max_attempts')));
64
+
65
+ // randomly order to prevent lock contention among workers
66
+ $collection->getSelect()->order(new Zend_Db_Expr('RAND()'));
67
+ $collection->load();
68
+
69
+ foreach($collection as $row) {
70
+ $job = new DJJob($this->workerName, $row->getId(), array(
71
+ "max_attempts" => Mage::getStoreConfig('jobqueue/config/max_attempts')
72
+ ));
73
+ if ($job->acquireLock()) {
74
+ $job->run();
75
+ }
76
+ }
77
+ } catch (Exception $e) {
78
+ Mage::logException($e);
79
+ }
80
+ }
81
+
82
+ protected function setupDJJob() {
83
+ $config = Mage::getConfig()->getResourceConnectionConfig("default_setup");
84
+
85
+ $dsn = "";
86
+ if (strpos($config->host, '/') !== false) {
87
+ $dsn = "mysql:unix_socket=" . $config->host . ";dbname=" . $config->dbname;
88
+ } else {
89
+ $dsn = "mysql:host=" . $config->host . ";dbname=" . $config->dbname . ";port=" . $config->port;
90
+ }
91
+
92
+ DJJob::configure(
93
+ $dsn,
94
+ array('mysql_user' => $config->username, 'mysql_pass' => $config->password)
95
+ );
96
+
97
+ if(!empty($config->initStatements)) {
98
+ DJJob::runQuery($config->initStatements);
99
+ }
100
+ }
101
  }
app/code/community/Jowens/JobQueue/controllers/Adminhtml/QueueController.php CHANGED
@@ -15,12 +15,12 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
15
  ->_title($this->__('System'))->_title($this->__('JobQueue'))
16
  ->_addBreadcrumb($this->__('System'), $this->__('System'))
17
  ->_addBreadcrumb($this->__('JobQueue'), $this->__('JobQueue'));
18
-
19
  return $this;
20
- }
21
 
22
  public function viewAction()
23
- {
24
  $id = $this->getRequest()->getParam('id');
25
  $job = Mage::getModel('jobqueue/job');
26
 
@@ -31,18 +31,18 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
31
  Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
32
  $this->_redirect('*/*/index');
33
  return;
34
- }
35
- }
36
-
37
  $this->_title($job->getId() ? $job->getName() : "Job Details");
38
-
39
  $data = Mage::getSingleton('adminhtml/session')->getJobData(true);
40
  if (!empty($data)) {
41
  $job->setData($data);
42
- }
43
-
44
  Mage::register('jowens_jobqueue_job', $job);
45
-
46
  $this->_init()
47
  ->renderLayout();
48
  }
@@ -68,7 +68,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
68
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be resubmitted', $job->getName()));
69
  }
70
  }
71
- $this->_redirect('*/*/index');
72
  }
73
 
74
  public function cancelAction()
@@ -92,7 +92,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
92
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be canceled', $job->getName()));
93
  }
94
  }
95
- $this->_redirect('*/*/index');
96
  }
97
 
98
  public function deleteAction()
@@ -107,7 +107,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
107
  Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
108
  $this->_redirect('*/*/index');
109
  return;
110
- }
111
 
112
  try {
113
  $job->delete();
@@ -115,9 +115,9 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
115
  } catch (Exception $e) {
116
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be deleted', $job->getName()));
117
  }
118
- }
119
- $this->_redirect('*/*/index');
120
- }
121
 
122
  public function massResubmitJobAction()
123
  {
@@ -131,7 +131,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
131
  $job->resubmit();
132
  $success++;
133
  } catch (Exception $e) {
134
- Mage::log($e);
135
  $error++;
136
  }
137
  }
@@ -140,10 +140,10 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
140
  if($error) {
141
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be resubmitted', $error));
142
  }
143
-
144
  if($success) {
145
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) resubmitted', $success));
146
- }
147
 
148
  $this->_redirect('*/*/index');
149
  }
@@ -164,7 +164,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
164
  $success++;
165
  }
166
  } catch (Exception $e) {
167
- Mage::log($e);
168
  $error++;
169
  }
170
  }
@@ -173,14 +173,13 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
173
  if($error) {
174
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be canceled', $error));
175
  }
176
-
177
  if($success) {
178
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) canceled', $success));
179
  }
180
-
181
 
182
  $this->_redirect('*/*/index');
183
- }
184
 
185
  public function massDeleteJobAction()
186
  {
@@ -194,7 +193,7 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
194
  $job->delete();
195
  $success++;
196
  } catch (Exception $e) {
197
- Mage::log($e);
198
  $error++;
199
  }
200
  }
@@ -203,10 +202,10 @@ class Jowens_JobQueue_Adminhtml_QueueController extends Mage_Adminhtml_Controlle
203
  if($error) {
204
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be deleted', $error));
205
  }
206
-
207
  if($success) {
208
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) deleted', $success));
209
- }
210
 
211
  $this->_redirect('*/*/index');
212
  }
15
  ->_title($this->__('System'))->_title($this->__('JobQueue'))
16
  ->_addBreadcrumb($this->__('System'), $this->__('System'))
17
  ->_addBreadcrumb($this->__('JobQueue'), $this->__('JobQueue'));
18
+
19
  return $this;
20
+ }
21
 
22
  public function viewAction()
23
+ {
24
  $id = $this->getRequest()->getParam('id');
25
  $job = Mage::getModel('jobqueue/job');
26
 
31
  Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
32
  $this->_redirect('*/*/index');
33
  return;
34
+ }
35
+ }
36
+
37
  $this->_title($job->getId() ? $job->getName() : "Job Details");
38
+
39
  $data = Mage::getSingleton('adminhtml/session')->getJobData(true);
40
  if (!empty($data)) {
41
  $job->setData($data);
42
+ }
43
+
44
  Mage::register('jowens_jobqueue_job', $job);
45
+
46
  $this->_init()
47
  ->renderLayout();
48
  }
68
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be resubmitted', $job->getName()));
69
  }
70
  }
71
+ $this->_redirect('*/*/index');
72
  }
73
 
74
  public function cancelAction()
92
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be canceled', $job->getName()));
93
  }
94
  }
95
+ $this->_redirect('*/*/index');
96
  }
97
 
98
  public function deleteAction()
107
  Mage::getSingleton('adminhtml/session')->addError($this->__('This job no longer exists.'));
108
  $this->_redirect('*/*/index');
109
  return;
110
+ }
111
 
112
  try {
113
  $job->delete();
115
  } catch (Exception $e) {
116
  Mage::getSingleton('adminhtml/session')->addError($this->__('Job "%s" could not be deleted', $job->getName()));
117
  }
118
+ }
119
+ $this->_redirect('*/*/index');
120
+ }
121
 
122
  public function massResubmitJobAction()
123
  {
131
  $job->resubmit();
132
  $success++;
133
  } catch (Exception $e) {
134
+ Mage::logException($e);
135
  $error++;
136
  }
137
  }
140
  if($error) {
141
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be resubmitted', $error));
142
  }
143
+
144
  if($success) {
145
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) resubmitted', $success));
146
+ }
147
 
148
  $this->_redirect('*/*/index');
149
  }
164
  $success++;
165
  }
166
  } catch (Exception $e) {
167
+ Mage::logException($e);
168
  $error++;
169
  }
170
  }
173
  if($error) {
174
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be canceled', $error));
175
  }
176
+
177
  if($success) {
178
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) canceled', $success));
179
  }
 
180
 
181
  $this->_redirect('*/*/index');
182
+ }
183
 
184
  public function massDeleteJobAction()
185
  {
193
  $job->delete();
194
  $success++;
195
  } catch (Exception $e) {
196
+ Mage::logException($e);
197
  $error++;
198
  }
199
  }
202
  if($error) {
203
  Mage::getSingleton('adminhtml/session')->addError($this->__('%s job(s) could not be deleted', $error));
204
  }
205
+
206
  if($success) {
207
  Mage::getSingleton('adminhtml/session')->addSuccess($this->__('%s job(s) deleted', $success));
208
+ }
209
 
210
  $this->_redirect('*/*/index');
211
  }
app/code/community/Jowens/JobQueue/etc/config.xml CHANGED
@@ -2,23 +2,23 @@
2
  <config>
3
  <modules>
4
  <Jowens_JobQueue>
5
- <version>0.2.0</version>
6
  </Jowens_JobQueue>
7
  </modules>
8
  <global>
9
  <models>
10
- <jobqueue>
11
- <class>Jowens_JobQueue_Model</class>
12
- <resourceModel>jobqueue_resource</resourceModel>
13
- </jobqueue>
14
- <jobqueue_resource>
15
- <class>Jowens_JobQueue_Model_Resource</class>
16
- <entities>
17
- <job>
18
- <table>jobs</table>
19
- </job>
20
- </entities>
21
- </jobqueue_resource>
22
  </models>
23
  <blocks>
24
  <jobqueue>
@@ -26,18 +26,18 @@
26
  </jobqueue>
27
  </blocks>
28
  <helpers>
29
- <jobqueue>
30
- <class>Jowens_JobQueue_Helper</class>
31
- </jobqueue>
32
  </helpers>
33
- <resources>
34
- <jobqueue_setup>
35
- <setup>
36
- <module>Jowens_JobQueue</module>
37
- <class>Jowens_JobQueue_Model_Resource_Setup</class>
38
- </setup>
39
- </jobqueue_setup>
40
- </resources>
41
  </global>
42
  <frontend>
43
  <routers>
@@ -71,17 +71,17 @@
71
  </layout>
72
  </adminhtml>
73
  <crontab>
74
- <jobs>
75
- <jobqueue_default>
76
- <schedule>
77
- <config_path>jobqueue/config/cron_expr</config_path>
78
- </schedule>
79
- <run>
80
- <model>jobqueue/worker::executeJobs</model>
81
- </run>
82
- </jobqueue_default>
83
- </jobs>
84
- </crontab>
85
  <default>
86
  <jobqueue>
87
  <config>
2
  <config>
3
  <modules>
4
  <Jowens_JobQueue>
5
+ <version>0.4.0</version>
6
  </Jowens_JobQueue>
7
  </modules>
8
  <global>
9
  <models>
10
+ <jobqueue>
11
+ <class>Jowens_JobQueue_Model</class>
12
+ <resourceModel>jobqueue_resource</resourceModel>
13
+ </jobqueue>
14
+ <jobqueue_resource>
15
+ <class>Jowens_JobQueue_Model_Resource</class>
16
+ <entities>
17
+ <job>
18
+ <table>jobs</table>
19
+ </job>
20
+ </entities>
21
+ </jobqueue_resource>
22
  </models>
23
  <blocks>
24
  <jobqueue>
26
  </jobqueue>
27
  </blocks>
28
  <helpers>
29
+ <jobqueue>
30
+ <class>Jowens_JobQueue_Helper</class>
31
+ </jobqueue>
32
  </helpers>
33
+ <resources>
34
+ <jobqueue_setup>
35
+ <setup>
36
+ <module>Jowens_JobQueue</module>
37
+ <class>Jowens_JobQueue_Model_Resource_Setup</class>
38
+ </setup>
39
+ </jobqueue_setup>
40
+ </resources>
41
  </global>
42
  <frontend>
43
  <routers>
71
  </layout>
72
  </adminhtml>
73
  <crontab>
74
+ <jobs>
75
+ <jobqueue_default>
76
+ <schedule>
77
+ <config_path>jobqueue/config/cron_expr</config_path>
78
+ </schedule>
79
+ <run>
80
+ <model>jobqueue/worker::executeJobs</model>
81
+ </run>
82
+ </jobqueue_default>
83
+ </jobs>
84
+ </crontab>
85
  <default>
86
  <jobqueue>
87
  <config>
app/code/community/Jowens/JobQueue/etc/system.xml CHANGED
@@ -13,16 +13,12 @@
13
  <frontend_type>text</frontend_type>
14
  <sort_order>2</sort_order>
15
  <show_in_default>1</show_in_default>
16
- <show_in_website>1</show_in_website>
17
- <show_in_store>1</show_in_store>
18
  <fields>
19
  <enabled translate="label comment">
20
  <label>Cron Worker Enabled</label>
21
  <frontend_type>select</frontend_type>
22
  <source_model>adminhtml/system_config_source_yesno</source_model>
23
  <show_in_default>1</show_in_default>
24
- <show_in_website>0</show_in_website>
25
- <show_in_store>0</show_in_store>
26
  </enabled>
27
  <cron_expr>
28
  <label>How often do you want the cron to run?</label>
@@ -30,28 +26,22 @@
30
  <sort_order>40</sort_order>
31
  <comment>Use Crontab Format (Eg. "*/5 * * * *" for every 5 minutes)</comment>
32
  <show_in_default>1</show_in_default>
33
- <show_in_website>0</show_in_website>
34
- <show_in_store>0</show_in_store>
35
  </cron_expr>
36
  <max_attempts>
37
  <label>Max Attempts</label>
38
  <frontend_type>text</frontend_type>
39
  <sort_order>50</sort_order>
40
  <show_in_default>1</show_in_default>
41
- <show_in_website>0</show_in_website>
42
- <show_in_store>0</show_in_store>
43
  </max_attempts>
44
  <queue>
45
  <label>Queue</label>
46
  <frontend_type>text</frontend_type>
47
  <sort_order>60</sort_order>
48
  <show_in_default>1</show_in_default>
49
- <show_in_website>0</show_in_website>
50
- <show_in_store>0</show_in_store>
51
  </queue>
52
  </fields>
53
  </config>
54
  </groups>
55
  </jobqueue>
56
  </sections>
57
- </config>
13
  <frontend_type>text</frontend_type>
14
  <sort_order>2</sort_order>
15
  <show_in_default>1</show_in_default>
 
 
16
  <fields>
17
  <enabled translate="label comment">
18
  <label>Cron Worker Enabled</label>
19
  <frontend_type>select</frontend_type>
20
  <source_model>adminhtml/system_config_source_yesno</source_model>
21
  <show_in_default>1</show_in_default>
 
 
22
  </enabled>
23
  <cron_expr>
24
  <label>How often do you want the cron to run?</label>
26
  <sort_order>40</sort_order>
27
  <comment>Use Crontab Format (Eg. "*/5 * * * *" for every 5 minutes)</comment>
28
  <show_in_default>1</show_in_default>
 
 
29
  </cron_expr>
30
  <max_attempts>
31
  <label>Max Attempts</label>
32
  <frontend_type>text</frontend_type>
33
  <sort_order>50</sort_order>
34
  <show_in_default>1</show_in_default>
 
 
35
  </max_attempts>
36
  <queue>
37
  <label>Queue</label>
38
  <frontend_type>text</frontend_type>
39
  <sort_order>60</sort_order>
40
  <show_in_default>1</show_in_default>
 
 
41
  </queue>
42
  </fields>
43
  </config>
44
  </groups>
45
  </jobqueue>
46
  </sections>
47
+ </config>
lib/DJJob/.gitignore ADDED
@@ -0,0 +1 @@
 
1
+ .DS_Store
lib/DJJob/DJJob.php CHANGED
@@ -28,19 +28,95 @@ class DJBase {
28
  private static $log_level = self::DEBUG;
29
 
30
  private static $db = null;
 
31
 
32
  private static $dsn = "";
33
- private static $options = array(
34
- "mysql_user" => null,
35
- "mysql_pass" => null,
36
- "mysql_retries" => 3
37
- );
38
 
39
  // use either `configure` or `setConnection`, depending on if
40
  // you already have a PDO object you can re-use
41
- public static function configure($dsn, $options = array()) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  self::$dsn = $dsn;
43
- self::$options = array_merge(self::$options, $options);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
 
46
  public static function setLogLevel($const) {
@@ -53,16 +129,8 @@ class DJBase {
53
 
54
  protected static function getConnection() {
55
  if (self::$db === null) {
56
- if (!self::$dsn) {
57
- throw new DJException("Please tell DJJob how to connect to your database by calling DJJob::configure(\$dsn, [\$options = array()]) or re-using an existing PDO connection by calling DJJob::setConnection(\$pdoObject). If you're using MySQL you'll need to pass the db credentials as separate 'mysql_user' and 'mysql_pass' options. This is a PDO limitation, see [http://stackoverflow.com/questions/237367/why-is-php-pdo-dsn-a-different-format-for-mysql-versus-postgresql] for an explanation.");
58
- }
59
  try {
60
- // http://stackoverflow.com/questions/237367/why-is-php-pdo-dsn-a-different-format-for-mysql-versus-postgresql
61
- if (self::$options["mysql_user"] !== null) {
62
- self::$db = new PDO(self::$dsn, self::$options["mysql_user"], self::$options["mysql_pass"]);
63
- } else {
64
- self::$db = new PDO(self::$dsn);
65
- }
66
  self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
67
  } catch (PDOException $e) {
68
  throw new Exception("DJJob couldn't connect to the database. PDO said [{$e->getMessage()}]");
@@ -72,9 +140,7 @@ class DJBase {
72
  }
73
 
74
  public static function runQuery($sql, $params = array()) {
75
- $retries = self::$options["mysql_retries"];
76
-
77
- for ($attempts = 0; $attempts < $retries; $attempts++) {
78
  try {
79
  $stmt = self::getConnection()->prepare($sql);
80
  $stmt->execute($params);
@@ -105,9 +171,7 @@ class DJBase {
105
  }
106
 
107
  public static function runUpdate($sql, $params = array()) {
108
- $retries = self::$options["mysql_retries"];
109
-
110
- for ($attempts = 0; $attempts < $retries; $attempts++) {
111
  try {
112
  $stmt = self::getConnection()->prepare($sql);
113
  $stmt->execute($params);
@@ -144,10 +208,11 @@ class DJWorker extends DJBase {
144
  "queue" => "default",
145
  "count" => 0,
146
  "sleep" => 5,
147
- "max_attempts" => 5
 
148
  ), $options);
149
- list($this->queue, $this->count, $this->sleep, $this->max_attempts) =
150
- array($options["queue"], $options["count"], $options["sleep"], $options["max_attempts"]);
151
 
152
  list($hostname, $pid) = array(trim(`hostname`), getmypid());
153
  $this->name = "host::$hostname pid::$pid";
@@ -172,7 +237,7 @@ class DJWorker extends DJBase {
172
 
173
  public function releaseLocks() {
174
  $this->runUpdate("
175
- UPDATE jobs
176
  SET locked_at = NULL, locked_by = NULL
177
  WHERE locked_by = ?",
178
  array($this->name)
@@ -190,7 +255,7 @@ class DJWorker extends DJBase {
190
  # we can grab a locked job if we own the lock
191
  $rs = $this->runQuery("
192
  SELECT id
193
- FROM jobs
194
  WHERE queue = ?
195
  AND (run_at IS NULL OR NOW() >= run_at)
196
  AND (locked_at IS NULL OR locked_by = ?)
@@ -205,7 +270,8 @@ class DJWorker extends DJBase {
205
 
206
  foreach ($rs as $r) {
207
  $job = new DJJob($this->name, $r["id"], array(
208
- "max_attempts" => $this->max_attempts
 
209
  ));
210
  if ($job->acquireLock()) return $job;
211
  }
@@ -246,11 +312,13 @@ class DJJob extends DJBase {
246
 
247
  public function __construct($worker_name, $job_id, $options = array()) {
248
  $options = array_merge(array(
249
- "max_attempts" => 5
 
250
  ), $options);
251
  $this->worker_name = $worker_name;
252
  $this->job_id = $job_id;
253
  $this->max_attempts = $options["max_attempts"];
 
254
  }
255
 
256
  public function run() {
@@ -264,8 +332,22 @@ class DJJob extends DJBase {
264
 
265
  # run the handler
266
  try {
 
 
 
 
 
267
  $handler->perform();
268
 
 
 
 
 
 
 
 
 
 
269
  # cleanup
270
  $this->finish();
271
  return true;
@@ -297,7 +379,7 @@ class DJJob extends DJBase {
297
  $this->log("[JOB] attempting to acquire lock for job::{$this->job_id} on {$this->worker_name}", self::INFO);
298
 
299
  $lock = $this->runUpdate("
300
- UPDATE jobs
301
  SET locked_at = NOW(), locked_by = ?
302
  WHERE id = ? AND (locked_at IS NULL OR locked_by = ?) AND failed_at IS NULL
303
  ", array($this->worker_name, $this->job_id, $this->worker_name));
@@ -312,7 +394,7 @@ class DJJob extends DJBase {
312
 
313
  public function releaseLock() {
314
  $this->runUpdate("
315
- UPDATE jobs
316
  SET locked_at = NULL, locked_by = NULL
317
  WHERE id = ?",
318
  array($this->job_id)
@@ -321,7 +403,7 @@ class DJJob extends DJBase {
321
 
322
  public function finish() {
323
  $this->runUpdate(
324
- "DELETE FROM jobs WHERE id = ?",
325
  array($this->job_id)
326
  );
327
  $this->log("[JOB] completed job::{$this->job_id}", self::INFO);
@@ -329,7 +411,7 @@ class DJJob extends DJBase {
329
 
330
  public function finishWithError($error, $handler = null) {
331
  $this->runUpdate("
332
- UPDATE jobs
333
  SET attempts = attempts + 1,
334
  failed_at = IF(attempts >= ?, NOW(), NULL),
335
  error = IF(attempts >= ?, ?, NULL)
@@ -352,7 +434,7 @@ class DJJob extends DJBase {
352
 
353
  public function retryLater($delay) {
354
  $this->runUpdate("
355
- UPDATE jobs
356
  SET run_at = DATE_ADD(NOW(), INTERVAL ? SECOND),
357
  attempts = attempts + 1
358
  WHERE id = ?",
@@ -366,7 +448,7 @@ class DJJob extends DJBase {
366
 
367
  public function getHandler() {
368
  $rs = $this->runQuery(
369
- "SELECT handler FROM jobs WHERE id = ?",
370
  array($this->job_id)
371
  );
372
  foreach ($rs as $r) return unserialize($r["handler"]);
@@ -375,7 +457,7 @@ class DJJob extends DJBase {
375
 
376
  public function getAttempts() {
377
  $rs = $this->runQuery(
378
- "SELECT attempts FROM jobs WHERE id = ?",
379
  array($this->job_id)
380
  );
381
  foreach ($rs as $r) return $r["attempts"];
@@ -384,7 +466,7 @@ class DJJob extends DJBase {
384
 
385
  public static function enqueue($handler, $queue = "default", $run_at = null) {
386
  $affected = self::runUpdate(
387
- "INSERT INTO jobs (handler, queue, run_at, created_at) VALUES(?, ?, ?, NOW())",
388
  array(serialize($handler), (string) $queue, $run_at)
389
  );
390
 
@@ -397,7 +479,7 @@ class DJJob extends DJBase {
397
  }
398
 
399
  public static function bulkEnqueue($handlers, $queue = "default", $run_at = null) {
400
- $sql = "INSERT INTO jobs (handler, queue, run_at, created_at) VALUES";
401
  $sql .= implode(",", array_fill(0, count($handlers), "(?, ?, ?, NOW())"));
402
 
403
  $parameters = array();
@@ -422,7 +504,7 @@ class DJJob extends DJBase {
422
  public static function status($queue = "default") {
423
  $rs = self::runQuery("
424
  SELECT COUNT(*) as total, COUNT(failed_at) as failed, COUNT(locked_at) as locked
425
- FROM `jobs`
426
  WHERE queue = ?
427
  ", array($queue));
428
  $rs = $rs[0];
28
  private static $log_level = self::DEBUG;
29
 
30
  private static $db = null;
31
+ protected static $jobsTable = "";
32
 
33
  private static $dsn = "";
34
+ private static $user = "";
35
+ private static $password = "";
36
+ private static $retries = 3; //default retries
 
 
37
 
38
  // use either `configure` or `setConnection`, depending on if
39
  // you already have a PDO object you can re-use
40
+
41
+ public static function configure(){
42
+ $args = func_get_args();
43
+ $numArgs = func_num_args();
44
+
45
+ switch ($numArgs) {
46
+ case 1:{
47
+ if (is_array($args[0])){
48
+ self::configureWithOptions($args[0]);
49
+ } else {
50
+ self::configureWithDsnAndOptions($args[0]);
51
+ }
52
+ break;
53
+ }
54
+ case 2:{
55
+ if (is_array($args[0])){
56
+ self::configureWithOptions($args[0], $args[1]);
57
+ } else {
58
+ self::configureWithDsnAndOptions($args[0], $args[1]);
59
+ }
60
+ break;
61
+ }
62
+ case 3: {
63
+ self::configureWithDsnAndOptions($args[0], $args[1], $args[2]);
64
+ break;
65
+ }
66
+ }
67
+ }
68
+
69
+ protected static function configureWithDsnAndOptions($dsn, array $options = array(), $jobsTable = 'jobs') {
70
+ if (!isset($options['mysql_user'])){
71
+ throw new DJException("Please provide the database user in configure options array.");
72
+ }
73
+ if (!isset($options['mysql_pass'])){
74
+ throw new DJException("Please provide the database password in configure options array.");
75
+ }
76
+
77
  self::$dsn = $dsn;
78
+ self::$jobsTable = $jobsTable;
79
+
80
+ self::$user = $options['mysql_user'];
81
+ self::$password = $options['mysql_pass'];
82
+
83
+ // searches for retries
84
+ if (isset($options['retries'])){
85
+ self::$retries = (int) $options['retries'];
86
+ }
87
+ }
88
+
89
+ protected static function configureWithOptions(array $options, $jobsTable = 'jobs') {
90
+
91
+ if (!isset($options['driver'])){
92
+ throw new DJException("Please provide the database driver used in configure options array.");
93
+ }
94
+ if (!isset($options['user'])){
95
+ throw new DJException("Please provide the database user in configure options array.");
96
+ }
97
+ if (!isset($options['password'])){
98
+ throw new DJException("Please provide the database password in configure options array.");
99
+ }
100
+
101
+ self::$user = $options['user'];
102
+ self::$password = $options['password'];
103
+ self::$jobsTable = $jobsTable;
104
+
105
+ self::$dsn = $options['driver'] . ':';
106
+ foreach ($options as $key => $value) {
107
+ // skips options already used
108
+ if ($key == 'driver' || $key == 'user' || $key == 'password') {
109
+ continue;
110
+ }
111
+
112
+ // searches for retries
113
+ if ($key == 'retries'){
114
+ self::$retries = (int) $value;
115
+ continue;
116
+ }
117
+
118
+ self::$dsn .= $key . '=' . $value . ';';
119
+ }
120
  }
121
 
122
  public static function setLogLevel($const) {
129
 
130
  protected static function getConnection() {
131
  if (self::$db === null) {
 
 
 
132
  try {
133
+ self::$db = new PDO(self::$dsn, self::$user, self::$password);
 
 
 
 
 
134
  self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
135
  } catch (PDOException $e) {
136
  throw new Exception("DJJob couldn't connect to the database. PDO said [{$e->getMessage()}]");
140
  }
141
 
142
  public static function runQuery($sql, $params = array()) {
143
+ for ($attempts = 0; $attempts < self::$retries; $attempts++) {
 
 
144
  try {
145
  $stmt = self::getConnection()->prepare($sql);
146
  $stmt->execute($params);
171
  }
172
 
173
  public static function runUpdate($sql, $params = array()) {
174
+ for ($attempts = 0; $attempts < self::$retries; $attempts++) {
 
 
175
  try {
176
  $stmt = self::getConnection()->prepare($sql);
177
  $stmt->execute($params);
208
  "queue" => "default",
209
  "count" => 0,
210
  "sleep" => 5,
211
+ "max_attempts" => 5,
212
+ "fail_on_output" => false
213
  ), $options);
214
+ list($this->queue, $this->count, $this->sleep, $this->max_attempts, $this->fail_on_output) =
215
+ array($options["queue"], $options["count"], $options["sleep"], $options["max_attempts"], $options["fail_on_output"]);
216
 
217
  list($hostname, $pid) = array(trim(`hostname`), getmypid());
218
  $this->name = "host::$hostname pid::$pid";
237
 
238
  public function releaseLocks() {
239
  $this->runUpdate("
240
+ UPDATE " . self::$jobsTable . "
241
  SET locked_at = NULL, locked_by = NULL
242
  WHERE locked_by = ?",
243
  array($this->name)
255
  # we can grab a locked job if we own the lock
256
  $rs = $this->runQuery("
257
  SELECT id
258
+ FROM " . self::$jobsTable . "
259
  WHERE queue = ?
260
  AND (run_at IS NULL OR NOW() >= run_at)
261
  AND (locked_at IS NULL OR locked_by = ?)
270
 
271
  foreach ($rs as $r) {
272
  $job = new DJJob($this->name, $r["id"], array(
273
+ "max_attempts" => $this->max_attempts,
274
+ "fail_on_output" => $this->fail_on_output
275
  ));
276
  if ($job->acquireLock()) return $job;
277
  }
312
 
313
  public function __construct($worker_name, $job_id, $options = array()) {
314
  $options = array_merge(array(
315
+ "max_attempts" => 5,
316
+ "fail_on_output" => false
317
  ), $options);
318
  $this->worker_name = $worker_name;
319
  $this->job_id = $job_id;
320
  $this->max_attempts = $options["max_attempts"];
321
+ $this->fail_on_output = $options["fail_on_output"];
322
  }
323
 
324
  public function run() {
332
 
333
  # run the handler
334
  try {
335
+
336
+ if ($this->fail_on_output) {
337
+ ob_start();
338
+ }
339
+
340
  $handler->perform();
341
 
342
+ if ($this->fail_on_output) {
343
+ $output = ob_get_contents();
344
+ ob_end_clean();
345
+
346
+ if (!empty($output)) {
347
+ throw new Exception("Job produced unexpected output: $output");
348
+ }
349
+ }
350
+
351
  # cleanup
352
  $this->finish();
353
  return true;
379
  $this->log("[JOB] attempting to acquire lock for job::{$this->job_id} on {$this->worker_name}", self::INFO);
380
 
381
  $lock = $this->runUpdate("
382
+ UPDATE " . self::$jobsTable . "
383
  SET locked_at = NOW(), locked_by = ?
384
  WHERE id = ? AND (locked_at IS NULL OR locked_by = ?) AND failed_at IS NULL
385
  ", array($this->worker_name, $this->job_id, $this->worker_name));
394
 
395
  public function releaseLock() {
396
  $this->runUpdate("
397
+ UPDATE " . self::$jobsTable . "
398
  SET locked_at = NULL, locked_by = NULL
399
  WHERE id = ?",
400
  array($this->job_id)
403
 
404
  public function finish() {
405
  $this->runUpdate(
406
+ "DELETE FROM " . self::$jobsTable . " WHERE id = ?",
407
  array($this->job_id)
408
  );
409
  $this->log("[JOB] completed job::{$this->job_id}", self::INFO);
411
 
412
  public function finishWithError($error, $handler = null) {
413
  $this->runUpdate("
414
+ UPDATE " . self::$jobsTable . "
415
  SET attempts = attempts + 1,
416
  failed_at = IF(attempts >= ?, NOW(), NULL),
417
  error = IF(attempts >= ?, ?, NULL)
434
 
435
  public function retryLater($delay) {
436
  $this->runUpdate("
437
+ UPDATE " . self::$jobsTable . "
438
  SET run_at = DATE_ADD(NOW(), INTERVAL ? SECOND),
439
  attempts = attempts + 1
440
  WHERE id = ?",
448
 
449
  public function getHandler() {
450
  $rs = $this->runQuery(
451
+ "SELECT handler FROM " . self::$jobsTable . " WHERE id = ?",
452
  array($this->job_id)
453
  );
454
  foreach ($rs as $r) return unserialize($r["handler"]);
457
 
458
  public function getAttempts() {
459
  $rs = $this->runQuery(
460
+ "SELECT attempts FROM " . self::$jobsTable . " WHERE id = ?",
461
  array($this->job_id)
462
  );
463
  foreach ($rs as $r) return $r["attempts"];
466
 
467
  public static function enqueue($handler, $queue = "default", $run_at = null) {
468
  $affected = self::runUpdate(
469
+ "INSERT INTO " . self::$jobsTable . " (handler, queue, run_at, created_at) VALUES(?, ?, ?, NOW())",
470
  array(serialize($handler), (string) $queue, $run_at)
471
  );
472
 
479
  }
480
 
481
  public static function bulkEnqueue($handlers, $queue = "default", $run_at = null) {
482
+ $sql = "INSERT INTO " . self::$jobsTable . " (handler, queue, run_at, created_at) VALUES";
483
  $sql .= implode(",", array_fill(0, count($handlers), "(?, ?, ?, NOW())"));
484
 
485
  $parameters = array();
504
  public static function status($queue = "default") {
505
  $rs = self::runQuery("
506
  SELECT COUNT(*) as total, COUNT(failed_at) as failed, COUNT(locked_at) as locked
507
+ FROM `" . self::$jobsTable . "`
508
  WHERE queue = ?
509
  ", array($queue));
510
  $rs = $rs[0];
lib/DJJob/README.textile CHANGED
@@ -31,9 +31,8 @@ bc. CREATE TABLE `jobs` (
31
 
32
  p. Tell DJJob how to connect to your database:
33
 
34
- bc. DJJob::configure("mysql:host=127.0.0.1;dbname=djjob_test;port=3306", array('mysql_user' => "root", 'mysql_pass' => "topsecret"));
35
-
36
- p. If you're using mysql, you'll need to pass the database credentials separately. Otherwise, you can provide those in the connection string, see "http://stackoverflow.com/questions/237367/why-is-php-pdo-dsn-a-different-format-for-mysql-versus-postgresql":http://stackoverflow.com/questions/237367/why-is-php-pdo-dsn-a-different-format-for-mysql-versus-postgresql for an explanation.
37
 
38
 
39
  h2. Usage
@@ -117,5 +116,4 @@ p. The worker will exit if the database has any connectivity problems. We use "g
117
 
118
  h3. Changes
119
 
120
- * Change DJJob::configure to take an options array
121
  * Eliminated Propel dependency by switching to PDO
31
 
32
  p. Tell DJJob how to connect to your database:
33
 
34
+ bc. DJJob::configure(['driver'=> 'mysql','host'=> '127.0.0.1','dbname'=> 'djjob','user'=> 'root','password'=> 'topsecret',
35
+ ]);
 
36
 
37
 
38
  h2. Usage
116
 
117
  h3. Changes
118
 
 
119
  * Eliminated Propel dependency by switching to PDO
lib/DJJob/test/custom_table_name.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function assert_handler($file, $line, $code, $desc = null) {
4
+ printf("Assertion failed at %s:%s: %s: %s\n", $file, $line, $code, $desc);
5
+ }
6
+
7
+ assert_options(ASSERT_ACTIVE, 1);
8
+ assert_options(ASSERT_WARNING, 0);
9
+ assert_options(ASSERT_QUIET_EVAL, 1);
10
+ assert_options(ASSERT_CALLBACK, 'assert_handler');
11
+
12
+ date_default_timezone_set('America/New_York');
13
+
14
+ require dirname(__FILE__) . "/../DJJob.php";
15
+
16
+ DJJob::configure([
17
+ 'driver' => 'mysql',
18
+ 'host' => '127.0.0.1',
19
+ 'dbname' => 'djjob',
20
+ 'user' => 'root',
21
+ 'password' => 'root',
22
+ ], 'my_jobs');
23
+
24
+ DJJob::runQuery("
25
+ DROP TABLE IF EXISTS `my_jobs`;
26
+ CREATE TABLE `my_jobs` (
27
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
28
+ `handler` VARCHAR(255) NOT NULL,
29
+ `queue` VARCHAR(255) NOT NULL DEFAULT 'default',
30
+ `attempts` INT UNSIGNED NOT NULL DEFAULT 0,
31
+ `run_at` DATETIME NULL,
32
+ `locked_at` DATETIME NULL,
33
+ `locked_by` VARCHAR(255) NULL,
34
+ `failed_at` DATETIME NULL,
35
+ `error` VARCHAR(255) NULL,
36
+ `created_at` DATETIME NOT NULL
37
+ ) ENGINE = MEMORY;
38
+ ");
39
+
40
+ class HelloWorldJob {
41
+ public function __construct($name) {
42
+ $this->name = $name;
43
+ }
44
+ public function perform() {
45
+ echo "Hello {$this->name}!\n";
46
+ sleep(1);
47
+ }
48
+ }
49
+
50
+ class FailingJob {
51
+ public function perform() {
52
+ sleep(1);
53
+ throw new Exception("Uh oh");
54
+ }
55
+ }
56
+
57
+ $status = DJJob::status();
58
+
59
+ assert('$status["outstanding"] == 0', "Initial outstanding status is incorrect");
60
+ assert('$status["locked"] == 0', "Initial locked status is incorrect");
61
+ assert('$status["failed"] == 0', "Initial failed status is incorrect");
62
+ assert('$status["total"] == 0', "Initial total status is incorrect");
63
+
64
+ printf("=====================\nStarting run of DJJob\n=====================\n\n");
65
+
66
+ DJJob::enqueue(new HelloWorldJob("delayed_job"));
67
+ DJJob::bulkEnqueue(array(
68
+ new HelloWorldJob("shopify"),
69
+ new HelloWorldJob("github"),
70
+ ));
71
+ DJJob::enqueue(new FailingJob());
72
+
73
+ $worker = new DJWorker(array("count" => 5, "max_attempts" => 2, "sleep" => 10));
74
+ $worker->start();
75
+ printf("\n============\nRun complete\n============\n\n");
76
+
77
+ $status = DJJob::status();
78
+
79
+ assert('$status["outstanding"] == 0', "Final outstanding status is incorrect");
80
+ assert('$status["locked"] == 0', "Final locked status is incorrect");
81
+ assert('$status["failed"] == 1', "Final failed status is incorrect");
82
+ assert('$status["total"] == 1', "Final total status is incorrect");
lib/DJJob/test/database.php CHANGED
@@ -13,10 +13,13 @@ date_default_timezone_set('America/New_York');
13
 
14
  require dirname(__FILE__) . "/../DJJob.php";
15
 
16
- DJJob::configure("mysql:host=127.0.0.1;dbname=djjob", array(
17
- "mysql_user" => "root",
18
- "mysql_pass" => "",
19
- ));
 
 
 
20
 
21
  DJJob::runQuery("
22
  DROP TABLE IF EXISTS `jobs`;
13
 
14
  require dirname(__FILE__) . "/../DJJob.php";
15
 
16
+ DJJob::configure([
17
+ 'driver' => 'mysql',
18
+ 'host' => '127.0.0.1',
19
+ 'dbname' => 'djjob',
20
+ 'user' => 'root',
21
+ 'password' => 'root',
22
+ ]);
23
 
24
  DJJob::runQuery("
25
  DROP TABLE IF EXISTS `jobs`;
lib/DJJob/test/original_database_configure.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function assert_handler($file, $line, $code, $desc = null) {
4
+ printf("Assertion failed at %s:%s: %s: %s\n", $file, $line, $code, $desc);
5
+ }
6
+
7
+ assert_options(ASSERT_ACTIVE, 1);
8
+ assert_options(ASSERT_WARNING, 0);
9
+ assert_options(ASSERT_QUIET_EVAL, 1);
10
+ assert_options(ASSERT_CALLBACK, 'assert_handler');
11
+
12
+ date_default_timezone_set('America/New_York');
13
+
14
+ require dirname(__FILE__) . "/../DJJob.php";
15
+
16
+ DJJob::configure("mysql:host=127.0.0.1;dbname=djjob;", array(
17
+ "mysql_user" => "root",
18
+ "mysql_pass" => "root",
19
+ ));
20
+
21
+ DJJob::runQuery("
22
+ DROP TABLE IF EXISTS `jobs`;
23
+ CREATE TABLE `jobs` (
24
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
25
+ `handler` VARCHAR(255) NOT NULL,
26
+ `queue` VARCHAR(255) NOT NULL DEFAULT 'default',
27
+ `attempts` INT UNSIGNED NOT NULL DEFAULT 0,
28
+ `run_at` DATETIME NULL,
29
+ `locked_at` DATETIME NULL,
30
+ `locked_by` VARCHAR(255) NULL,
31
+ `failed_at` DATETIME NULL,
32
+ `error` VARCHAR(255) NULL,
33
+ `created_at` DATETIME NOT NULL
34
+ ) ENGINE = MEMORY;
35
+ ");
36
+
37
+ class HelloWorldJob {
38
+ public function __construct($name) {
39
+ $this->name = $name;
40
+ }
41
+ public function perform() {
42
+ echo "Hello {$this->name}!\n";
43
+ sleep(1);
44
+ }
45
+ }
46
+
47
+ class FailingJob {
48
+ public function perform() {
49
+ sleep(1);
50
+ throw new Exception("Uh oh");
51
+ }
52
+ }
53
+
54
+ $status = DJJob::status();
55
+
56
+ assert('$status["outstanding"] == 0', "Initial outstanding status is incorrect");
57
+ assert('$status["locked"] == 0', "Initial locked status is incorrect");
58
+ assert('$status["failed"] == 0', "Initial failed status is incorrect");
59
+ assert('$status["total"] == 0', "Initial total status is incorrect");
60
+
61
+ printf("=====================\nStarting run of DJJob\n=====================\n\n");
62
+
63
+ DJJob::enqueue(new HelloWorldJob("delayed_job"));
64
+ DJJob::bulkEnqueue(array(
65
+ new HelloWorldJob("shopify"),
66
+ new HelloWorldJob("github"),
67
+ ));
68
+ DJJob::enqueue(new FailingJob());
69
+
70
+ $worker = new DJWorker(array("count" => 5, "max_attempts" => 2, "sleep" => 10));
71
+ $worker->start();
72
+ printf("\n============\nRun complete\n============\n\n");
73
+
74
+ $status = DJJob::status();
75
+
76
+ assert('$status["outstanding"] == 0', "Final outstanding status is incorrect");
77
+ assert('$status["locked"] == 0', "Final locked status is incorrect");
78
+ assert('$status["failed"] == 1', "Final failed status is incorrect");
79
+ assert('$status["total"] == 1', "Final total status is incorrect");
package.xml CHANGED
@@ -1,18 +1,18 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Jowens_JobQueue</name>
4
- <version>0.3.0</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/MIT">MIT</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Asynchronous job queue for Magento</summary>
10
  <description>JobQueue allows jobs to be queued in the database to be processed asynchronously.</description>
11
- <notes>Fixed issue executing queued jobs.</notes>
12
  <authors><author><name>Jordan Owens</name><user>jkowens</user><email>jkowens@gmail.com</email></author></authors>
13
- <date>2014-01-07</date>
14
- <time>22:32:42</time>
15
- <contents><target name="magecommunity"><dir name="Jowens"><dir name="JobQueue"><dir name="Block"><dir name="Adminhtml"><dir name="Job"><file name="View.php" hash="eb45668ba7fcb8e558ad87b7d21b87ac"/></dir><dir name="Queue"><file name="Grid.php" hash="9618ecc67272f750c88e7e19680aaa44"/></dir><file name="Queue.php" hash="719a95c4bde9c4c109b159d0667de2da"/></dir></dir><dir name="Helper"><file name="Data.php" hash="8c8b3a2b79a1546e8b6600dde974c049"/></dir><dir name="Model"><dir name="Job"><file name="Abstract.php" hash="464af3c758f6c94fc768f48379bf287e"/></dir><file name="Job.php" hash="f0b9928c1063dc3d90087fb69de16fa4"/><dir name="Resource"><dir name="Job"><file name="Collection.php" hash="a175198f2c3d252dadb817d108920b3f"/></dir><file name="Job.php" hash="30843d577f463d43ed4e3807187e8248"/><file name="Setup.php" hash="911413029124da3964d9822926fb44de"/></dir><file name="Worker.php" hash="7ca65f4a22f58b85b44d08fe01e2e710"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="QueueController.php" hash="c3134b96774c9d3af9b50835fdd3463a"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="885171a59394683e2a8ec1d5701c0817"/><file name="config.xml" hash="826804a99f7ee2ce96aaabbdc87df3cb"/><file name="system.xml" hash="65f3b71d70492f76097d993053ee4d44"/></dir><dir name="sql"><dir name="jobqueue_setup"><file name="mysql4-install-0.1.0.php" hash="19592c4c921f2e1c64e85c1a776edda2"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Jowens_JobQueue.xml" hash="272f42382ccc1b0226c7e25c078d54ae"/></dir></target><target name="magelib"><dir name="DJJob"><file name="DJJob.php" hash="c2a742d0e2cb43208844636b243ec8ae"/><file name="LICENSE" hash="34cf8e3fef5d267eb53ad593d4e14dd3"/><file name="README.textile" hash="ae3feeccf3476b207a05894aabf4afaf"/><file name="composer.json" hash="101e6cb50439389d03c73b7a5179dc0a"/><dir name="examples"><file name="HelloWorldJob.php" hash="3b7a9e4b1f912fb48acf5399f5fe33b9"/></dir><file name="jobs.sql" hash="d73a8213feedadf9dc9eb719fe33b935"/><dir name="test"><file name="database.php" hash="c130f97354004a05752ec114e9d2c9eb"/></dir><file name=".git" hash="6efe08f8b224dddac89f9229e7ccd042"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="jowens"><file name="jobqueue.xml" hash="491d99d8da67cc879386dda4ef90f285"/></dir></dir><dir name="template"><dir name="jowens"><dir name="jobqueue"><file name="job.phtml" hash="1511cf6f2b85b62f77d6ce741004875e"/></dir></dir></dir></dir></dir></dir></target></contents>
16
  <compatible/>
17
- <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.6.0</min><max></max></package></required></dependencies>
18
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Jowens_JobQueue</name>
4
+ <version>0.4.0</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/MIT">MIT</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Asynchronous job queue for Magento</summary>
10
  <description>JobQueue allows jobs to be queued in the database to be processed asynchronously.</description>
11
+ <notes>Update DJJob and fix timezone issues.</notes>
12
  <authors><author><name>Jordan Owens</name><user>jkowens</user><email>jkowens@gmail.com</email></author></authors>
13
+ <date>2015-06-19</date>
14
+ <time>20:45:26</time>
15
+ <contents><target name="magecommunity"><dir name="Jowens"><dir name="JobQueue"><dir name="Block"><dir name="Adminhtml"><dir name="Job"><file name="View.php" hash="ff834b5f23eaefde8c133a2ba2c537b8"/></dir><dir name="Queue"><file name="Grid.php" hash="9618ecc67272f750c88e7e19680aaa44"/></dir><file name="Queue.php" hash="719a95c4bde9c4c109b159d0667de2da"/></dir></dir><dir name="Helper"><file name="Data.php" hash="8c8b3a2b79a1546e8b6600dde974c049"/></dir><dir name="Model"><dir name="Job"><file name="Abstract.php" hash="18e503f408b789cbeac44703b0e350ae"/></dir><file name="Job.php" hash="f0b9928c1063dc3d90087fb69de16fa4"/><dir name="Resource"><dir name="Job"><file name="Collection.php" hash="a175198f2c3d252dadb817d108920b3f"/></dir><file name="Job.php" hash="30843d577f463d43ed4e3807187e8248"/><file name="Setup.php" hash="911413029124da3964d9822926fb44de"/></dir><file name="Worker.php" hash="eed5b3f7bd6536cc3095dec40df9d5e9"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="QueueController.php" hash="835ea1d30d93be44da0aba27b07707c6"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="885171a59394683e2a8ec1d5701c0817"/><file name="config.xml" hash="949f07683d7127d60be30733dda024cd"/><file name="system.xml" hash="3a8426d2f3c9a3f29d4adff6733d3ea9"/></dir><dir name="sql"><dir name="jobqueue_setup"><file name="mysql4-install-0.1.0.php" hash="19592c4c921f2e1c64e85c1a776edda2"/></dir></dir></dir><dir name="JobQueue.bak"><dir name="Block"><dir name="Adminhtml"><dir name="Job"><file name="View.php" hash="238f4cf80a59075c15859d7adeb3c466"/></dir><dir name="Queue"><file name="Grid.php" hash="9618ecc67272f750c88e7e19680aaa44"/></dir><file name="Queue.php" hash="719a95c4bde9c4c109b159d0667de2da"/></dir></dir><dir name="Helper"><file name="Data.php" hash="8c8b3a2b79a1546e8b6600dde974c049"/></dir><dir name="Model"><dir name="Job"><file name="Abstract.php" hash="464af3c758f6c94fc768f48379bf287e"/></dir><file name="Job.php" hash="f0b9928c1063dc3d90087fb69de16fa4"/><dir name="Order"><file name="Job.php" hash="bbbb502473c2ffeaefe79efe5ccfd25e"/></dir><dir name="Resource"><dir name="Job"><file name="Collection.php" hash="a175198f2c3d252dadb817d108920b3f"/></dir><file name="Job.php" hash="30843d577f463d43ed4e3807187e8248"/><file name="Setup.php" hash="911413029124da3964d9822926fb44de"/></dir><file name="Worker.php" hash="7ca65f4a22f58b85b44d08fe01e2e710"/><file name=".DS_Store" hash="c01be101f59db5c5ac332055316bf583"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="QueueController.php" hash="1ec3ff407c334b38217dfca0d11012ee"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="885171a59394683e2a8ec1d5701c0817"/><file name="config.xml" hash="826804a99f7ee2ce96aaabbdc87df3cb"/><file name="system.xml" hash="65f3b71d70492f76097d993053ee4d44"/></dir><dir name="sql"><dir name="jobqueue_setup"><file name="mysql4-install-0.1.0.php" hash="19592c4c921f2e1c64e85c1a776edda2"/></dir><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Jowens_JobQueue.xml" hash="272f42382ccc1b0226c7e25c078d54ae"/></dir></target><target name="magelib"><dir name="DJJob"><file name="DJJob.php" hash="b692e8d7a53a8a7096ea60d2cfd237f0"/><file name="LICENSE" hash="34cf8e3fef5d267eb53ad593d4e14dd3"/><file name="README.textile" hash="3ca7409668a09a7ef6c1a315eadd9851"/><file name="composer.json" hash="101e6cb50439389d03c73b7a5179dc0a"/><dir name="examples"><file name="HelloWorldJob.php" hash="3b7a9e4b1f912fb48acf5399f5fe33b9"/></dir><file name="jobs.sql" hash="d73a8213feedadf9dc9eb719fe33b935"/><dir name="test"><file name="custom_table_name.php" hash="3fbebd54f7814c9797107014c9cf0228"/><file name="database.php" hash="82e80af8b072a6276cd46dc4d09f937b"/><file name="original_database_configure.php" hash="e3acccdc25c6f876467c35729505626f"/></dir><file name=".git" hash="1b09a2847a4dbdd7688f4e38637dae68"/><file name=".gitignore" hash="952fd44d14cee87882239b707231609d"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="jowens"><file name="jobqueue.xml" hash="491d99d8da67cc879386dda4ef90f285"/></dir></dir><dir name="template"><dir name="jowens"><dir name="jobqueue"><file name="job.phtml" hash="1511cf6f2b85b62f77d6ce741004875e"/></dir></dir></dir></dir></dir></dir></target></contents>
16
  <compatible/>
17
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.6.0</min><max/></package></required></dependencies>
18
  </package>