Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 225 lines (157 sloc) 9.223 kB
f719dc1 @fterrier original version
authored
1 Tasks plugin
2 ===
20f0a88 @fterrier Initial commit
authored
3
f719dc1 @fterrier original version
authored
4 The tasks plugin provides a way to run background tasks in grails using [rabbitmq][rabbitmq] to queue them. Provides
844e7f1 @fterrier removed redundant fields and added documentation
authored
5 a framework for adding tasks, deleting them, and aborting them. Provides the option to create tasks that take
6 a file as an input and that produce output files.
f719dc1 @fterrier original version
authored
7 [rabbitmq]: http://www.rabbitmq.com/
8
844e7f1 @fterrier removed redundant fields and added documentation
authored
9 Installation
10 ---
11
12 To install this plugin, run the following:
13
14 grails install-plugin tasks
15
16 You also need to install rabbitmq-server. To do that, follow the instructions on [rabbitmq.com](http://www.rabbitmq.com/).
17
18 Configuration
19 ---
20
21 There is one configuration option that can be set to specify the directory for temporary output files:
22
afd21a2 @fterrier Update README.md
authored
23 grails.tasks.task.temp.folder="/tmp"
844e7f1 @fterrier removed redundant fields and added documentation
authored
24
25 Create a simple task with no input/output
26 ---
27
28 To create a new task, you just need to extend the Task abstract class and provide an implementation for the ``executeTask()`` and ``isUnique()`` methods.
29
30 class MyTask extends Task {
31
32 void executeTask() {
33 // code that runs the task
34 }
35
36 boolean isUnique() {
37 // return true if there exists already a task
38 // that will have the same effect as this one
39 // otherwise return false
40 }
41
42 }
43
44 **The implementation should be in the package ``org.chai.task``**.
45
46 Once a task is succesfully created, it is persisted in the database, and automatically sent to rabbitmq if rabbitmq-server is running. Depending on rabbitmq configuration, the tasks are picked up by 1 or several workers and executed in the background.
47
48 If the server is stopped, tasks will be interrupted and restarted at startup. If rabbitmq is not running, a cron job will attend to send the tasks to the queue every 2 minutes.
49
50 Once you've added the code that runs the task, you can use actions of the ``TaskController`` to create and delete tasks. The controller provides the following action:
51
52 def create = {
53 // Creates a simple task:
54 // - takes a 'class' parameter that specifies the
55 // non-qualified name of the Task implementation class
56 // - all other params will be bound to the Task
57 // implementation class using bindParams(…)
58 }
59
60 Thus, calling ``task/create?class=MyTask`` will create an instance of the ``MyTask`` class, and will send it to rabbitmq for processing. When the task is picked up from the queue, the ``executeTask`` method will be run.
61
62 You can add an arbitrary number of named parameters to your class and pass them as a request parameter to the ``create`` action. For instance, let's modify the class as follows:
63
64 class MyTask extends Task {
65
66 Integer param1
67
68 ...
69 }
70
71 Calling ``task/create?class=MyTask&param1=10`` will bind 10 to the new instance ``param1`` field.
72
73 Create a task that takes a file as an input
74 ---
75
76 To create a task that takes a file as an input, you need to implement the 2 extra methods ``getFormView()`` and ``getFormModel()``, that specify where to find the view and the model for the form that will upload the file.
77
78 class MyTask extends Task {
79
80 String getFormView() {
81 // returns the name of the view that will display the form
82 }
83
84 Map getFormModel() {
85 // returns the model that will be used to display the form
86 }
87
88 ...
89 }
90
91 The view might look something like this:
92
93 <g:form url="[controller:'task', action:'createTaskWithFile']">
94 <input type="hidden" name="class" value="MyTask"/>
95 <input type="file" name="file"
96 value="${fieldValue(bean:task, field:'file')}"/>
97
98 <button type="submit">Upload</button>
99 </g:form>
100
101 Along with the following model:
102
103 Map getFormModel() {
104 return [task: this]
105 }
106
107 The only restriction placed on the form view is that the form should contain an input field of type file named ``file``.
108
109 The ``TaskController`` provides two actions to display the upload form and to create the task.
110
111 def taskForm = {
112 // Displays the task creation form
113 // - takes a 'class' parameter that specifies the
114 // non-qualified name of the Task implementation class
115 }
116
117 def createTaskWithFile = {
118 // Creates a task with a file from POSTing the
119 // task creation form
120 // - takes a 'class' parameter that specifies the
121 // non-qualified name of the Task implementation class
122 // - expects an input file named 'file' in the POST
123 // - all other params are bound to the Task
124 // implementation class using bindParams(…)
125 }
126
127 Calling the ``task/taskForm?class=MyTask`` URL will simply display the specified form and model defined in the Task implementation. That form should POST to ``task/createTaskWithFile``. Submitting the form will then create an instance of the class and save the file in the temporary directory specified in the configuration (cf. Configuration above) and will send it to rabbitmq.
128
129 Create a task that produces an output file
130 ---
131
132 The plugin provides a mechanism for creating output files. If the Task implementation class provides an implemenation for the ``getOutputFilename()`` method, it means the task produces an output file that can be retrieved under that name.
133
134 String getOutputFilename() {
135 // returns the name of a the output file
136 }
137
138 It is the job of the task implementation to create the file and save the output to the file specified by the ``getOutputFilename()`` method. That file should be placed in the folder given by the Task abstract class ``getFolder()`` method. A Task that creates an output could look like this:
139
140 def executeTask() {
141 String outputContent = "this is the output"
142 File outputFile = new File(getFolder(), getOutputFilename())
143 outputFile.createNewFile()
144
145 def fileWriter = new FileWriter(outputFile)
146 IOUtils.write(errorOutput, fileWriter)
147 fileWriter.flush()
148 IOUtils.closeQuietly(fileWriter)
149 }
150
151 The ``TaskController`` provides a method to download the output file, zipped.
152
153 def downloadOutput = {
154 // If the task creates an output file, this sends the
155 // output as a zip file to the outputStream.
156 // - takes a 'id' parameter that specify the task for which
157 // the output should be downloaded
158 }
159
160 Calling ``task/downloadOutput?id=<task_id>`` will download the file.
161
162 Task status and progress
163 ---
164
165 At any time, the status of a task can be retrieved by calling ``task.status``. There are 4 possible statuses, ``NEW``, ``COMPLETED``, ``IN_PROGRESS`` or ``ABORTED``. When a task is first created, its status is set to ``NEW``. Once it's picked up by the rabbitmq worker, it's set to ``IN_PROGRESS``. Once a task is finished, its status is set to ``COMPLETED``. If the task ``abort()`` method is called, the status is set to ``ABORTED``.
166
167 Beside a status, the Task class provides the functionality to set and increment a progress while the task is running. The Task class provides the following 2 methods:
168
169 void setMaximum(Long max) {
170 // sets the maximum progress of this task to 'max'
171 }
172
173 void incrementProgress(Long increment = null) {
174 // increment the progress of that task by 'increment', or
175 // by 1, if 'increment' is null
176 }
177
178 It is obviously the job of the implementation to set the maximum number of steps it will take to reach 100% completion, as well as to increment the progress. The ``TaskController`` provides an action to interrogate the progress of 1 or several tasks:
179
180 def progress = {
181 // Returns a JSON response with the progress
182 // for the specified tasks. The response has the format
183 // [
184 // {id: <task_id>, status: <task_status>, progress: <task_progress},
185 // {…}
186 // ]
187 // - takes a 'ids' parameter specifying a list of tasks
188 // to query for progress
189 }
190
191 Deleting tasks
192 ---
193
194 Tasks that are not currently running (the status is not ``IN_PROGRESS``) can be deleted from the database using the ``TaskController`` delete action.
195
196 def delete = {
197 // Deletes the specified task
198 // - takes a 'id' parameter that specify the task that should
199 // be deleted
200 }
201
202 Using this action, a task is removed from the database, but not from the rabbitmq queue. However, when the worker picks up the tasks, it will notice that the task has been deleted and will not execute it.
203
204 Another action allows one to delete all ``COMPLETED`` tasks from the database.
205
206 def purge = {
207 // Purges all COMPLETED tasks
208 }
3d0d391 @fterrier added security provider info in README file
authored
209
210 Security Provider
211 ---
212
213 The Tasks plugin uses the [Platform Core] to store the user identity as the ``principal`` property on the Task created using the ``TaskController`` if a Security API implementation is provided.
214
215 An example of how to implement the Platform Core [SecurityBridge] interface for the [Shiro] plugin is provided in the plugin test file [ShiroSecurityBridge] for you to check out.
216 [Shiro]: http://grails.org/plugin/shiro
217 [Platform Core]: http://grails.org/plugin/platform-core
218 [SecurityBridge]: http://grailsrocks.github.com/grails-platform-core/gapi/org/grails/plugin/platform/security/SecurityBridge.html
219 [ShiroSecurityBridge]: https://github.com/fterrier/grails-rabbitmq-tasks/blob/master/test/projects/test-task-project/grails-app/services/org/chai/task/ShiroSecurityBridge.groovy
844e7f1 @fterrier removed redundant fields and added documentation
authored
220
f719dc1 @fterrier original version
authored
221 License
222 ---
223
224 The task plugin is licensed under the terms of the [BSD 3-clause License][BSD 3-clause License].
225 [BSD 3-clause License]: http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html
Something went wrong with that request. Please try again.