001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.asyncTasks;
019
020import io.stallion.Context;
021import io.stallion.dataAccess.ModelBase;
022import io.stallion.dataAccess.UniqueKey;
023import io.stallion.plugins.javascript.JsAsyncTaskHandler;
024import io.stallion.services.Log;
025import io.stallion.utils.json.JSON;
026
027import javax.persistence.Column;
028import javax.persistence.Table;
029
030import static io.stallion.utils.Literals.empty;
031
032@Table(name="stallion_async_tasks")
033public class AsyncTask extends ModelBase implements Comparable<AsyncTask> {
034    private long createdAt = 0;
035    private long updatedAt = 0;
036    private String handlerName;
037    private String customKey = null;
038    private long lockedAt = 0;
039    private long failedAt = 0;
040    private long completedAt = 0;
041    private long originallyScheduledFor;
042    private long executeAt = 0;
043    private boolean neverRetry = false;
044    private String lockUuid = "";
045    private String secret = "";
046    private int tryCount = 0;
047    private String errorMessage = "";
048    private String dataJson = "";
049    private String localMode = "";
050
051
052    public static AsyncTaskController controller() {
053        return (AsyncTaskController)Context.dal().get("st-async-async-tasks");
054    }
055
056    public AsyncTask() {
057
058    }
059
060    public AsyncTask(AsyncTaskHandler handler) {
061        this(handler, null, 0);
062
063    }
064
065    public AsyncTask(AsyncTaskHandler handler, String customKey, long executeAt) {
066        setHandler(handler).setCustomKey(customKey).setExecuteAt(executeAt);
067    }
068
069    public AsyncTask enqueue() {
070        AsyncCoordinator.instance().enqueue(this);
071        return this;
072    }
073
074    /**
075     * When the task was created, in epoch milliseconds
076     * @return
077     */
078    @Column
079    public long getCreatedAt() {
080        return createdAt;
081    }
082
083    public AsyncTask setCreatedAt(long createdAt) {
084        this.createdAt = createdAt;
085        return this;
086    }
087
088    /**
089     * When the task was last updated, in epoch milliseconds
090     * @return
091     */
092    @Column
093    public long getUpdatedAt() {
094        return updatedAt;
095    }
096
097    public AsyncTask setUpdatedAt(long updatedAt) {
098        this.updatedAt = updatedAt;
099        return this;
100    }
101
102    /**
103     * The name of the handler class. When loading a task after reboot, the handler class
104     * will be looked up and loaded by using this name.
105     *
106     * @return
107     */
108    @Column
109    public String getHandlerName() {
110        return handlerName;
111    }
112
113    /**
114     * Will use the handler class name, for later reloading, and then convert the handler to JSON
115     * and assign the data to the dataJson field of this task.
116     * @param handler
117     * @return
118     */
119    public AsyncTask setHandler(AsyncTaskHandler handler) {
120
121        if (handler instanceof JsAsyncTaskHandler) {
122            setHandlerName(((JsAsyncTaskHandler) handler).getHandlerClassName());
123            setDataJson(JSON.stringify(((JsAsyncTaskHandler) handler).getInternalMap()));
124            Log.info("STRIFIGYD {0}", JSON.stringify(((JsAsyncTaskHandler) handler).getInternalMap()));
125        } else {
126            setHandlerName(handler.getClass().getCanonicalName());
127            setDataJson(JSON.stringify(handler));
128        }
129
130        return this;
131    }
132
133    public AsyncTask setHandlerName(String handlerName) {
134        this.handlerName = handlerName;
135        return this;
136    }
137
138    /**
139     * A user generated unique key for the task, used for updating the task or preventing duplicates.
140     * @return
141     */
142    @Column
143    @UniqueKey
144    public String getCustomKey() {
145        return customKey;
146    }
147
148    public AsyncTask setCustomKey(String customKey) {
149        this.customKey = customKey;
150        return this;
151    }
152
153    /**
154     * When the task was locked, in epoch milliseconds
155     * @return
156     */
157    @Column
158    public long getLockedAt() {
159        return lockedAt;
160    }
161
162    public AsyncTask setLockedAt(long lockedAt) {
163        this.lockedAt = lockedAt;
164        return this;
165    }
166
167    /**
168     * When the task last failed, in epoch milliseconds
169     * @return
170     */
171    @Column
172    public long getFailedAt() {
173        return failedAt;
174    }
175
176    public AsyncTask setFailedAt(long failedAt) {
177        this.failedAt = failedAt;
178        return this;
179    }
180
181    /**
182     * When the task successfully completed, in epoch milliseconds
183     * @return
184     */
185    @Column
186    public long getCompletedAt() {
187        return completedAt;
188    }
189
190    public AsyncTask setCompletedAt(long completedAt) {
191        this.completedAt = completedAt;
192        return this;
193    }
194
195    /**
196     * When the task was scheduled for originally, before any failures that made the
197     * coordinator reschedule it for a retried execution. For a task that has never been
198     * run, this time will equal the executeAt time.
199     *
200     * @return
201     */
202    @Column
203    public long getOriginallyScheduledFor() {
204        return originallyScheduledFor;
205    }
206
207    public AsyncTask setOriginallyScheduledFor(long originallyScheduledFor) {
208        this.originallyScheduledFor = originallyScheduledFor;
209        return this;
210    }
211
212    /**
213     * When the task should execute, in epoch milliseconds.
214     *
215     * @return
216     */
217    @Column
218    public long getExecuteAt() {
219        return executeAt;
220    }
221
222    public AsyncTask setExecuteAt(long executeAt) {
223        this.executeAt = executeAt;
224        return this;
225    }
226
227    /**
228     * If true, the task should never be retried on failure.
229     * @return
230     */
231    @Column
232    public boolean isNeverRetry() {
233        return neverRetry;
234    }
235
236    public AsyncTask setNeverRetry(boolean neverRetry) {
237        this.neverRetry = neverRetry;
238        return this;
239    }
240
241    /**
242     * A unique lock key, generated by the async persister when locking a task.
243     * @return
244     */
245    @Column
246    public String getLockUuid() {
247        return lockUuid;
248    }
249
250    public AsyncTask setLockUuid(String lockUuid) {
251        this.lockUuid = lockUuid;
252        return this;
253    }
254
255    /**
256     * A secret key, generated by the async persister, that can be used for
257     * looking up the task object.
258     * @return
259     */
260    @Column
261    @UniqueKey
262    public String getSecret() {
263        return secret;
264    }
265
266    public AsyncTask setSecret(String secret) {
267        this.secret = secret;
268        return this;
269    }
270
271    /**
272     * How many times the task has been tried to execute, incremented every time the task fails.
273     *
274     * @return
275     */
276    @Column
277    public int getTryCount() {
278        return tryCount;
279    }
280
281    public AsyncTask setTryCount(int tryCount) {
282        this.tryCount = tryCount;
283        return this;
284    }
285
286    /**
287     * The stack trace from the last failure.
288     * @return
289     */
290    @Column
291    public String getErrorMessage() {
292        return errorMessage;
293    }
294
295    public AsyncTask setErrorMessage(String errorMessage) {
296        this.errorMessage = errorMessage;
297        return this;
298    }
299
300    /**
301     * The async handler instance will be serialized to this field during task creation. When the task
302     * is executed, the this field will be deserialized and used to hydrate the fields of the
303     * handler class.
304     * @return
305     */
306    @Column
307    public String getDataJson() {
308        return dataJson;
309    }
310
311    public AsyncTask setDataJson(String data) {
312        this.dataJson = data;
313        return this;
314    }
315
316    /**
317     * Parses getDataJson() and returns the resulting class.
318     *
319     * @param cls
320     * @param <V>
321     * @return
322     */
323    public <V> V getData(Class<? extends V> cls) {
324        if (!empty(this.dataJson )) {
325            return JSON.parse(this.dataJson, cls);
326        } else {
327            return null;
328        }
329    }
330
331    /**
332     * JSON Stringifys the object and sets this.dataJson
333     *
334     * @param o
335     */
336    public void setData(Object o) {
337        this.dataJson = JSON.stringify(o);
338    }
339
340    /**
341     * Implements the comparable interface to see which task should be executed sooner.
342     *
343     * @param o
344     * @return
345     */
346    @Override
347    public int compareTo(AsyncTask o) {
348        return Long.compare(this.getExecuteAt(), o.getExecuteAt());
349    }
350
351    public String getLocalMode() {
352        return localMode;
353    }
354
355    public AsyncTask setLocalMode(String localMode) {
356        this.localMode = localMode;
357        return this;
358    }
359}