001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.core.work.api;
018
019import java.io.Serializable;
020import java.util.List;
021
022import org.nuxeo.ecm.core.api.DocumentLocation;
023import org.nuxeo.ecm.core.api.IdRef;
024import org.nuxeo.ecm.core.work.AbstractWork;
025import org.nuxeo.ecm.core.work.api.WorkManager.Scheduling;
026
027/**
028 * A {@link Work} instance gets scheduled and executed by a {@link WorkManager} .
029 * <p>
030 * Its {@link #work} method runs when a slot in a queue becomes available, note however that it can be suspended at any
031 * time (before initial execution, or during execution).
032 * <p>
033 * A {@link Work} instance has an id that is used by the queuing mechanisms to determine uniqueness. It also has a
034 * category that is used to choose which queue should execute it. It can report its status and progress.
035 * <p>
036 * A {@link Work} instance is Serializable because it must be able to save its computation state on interruption, and be
037 * reconstructed again with the saved state to continue execution at a later time. Because of this, the instance itself
038 * may change over time and be executed on a different JVM than the one that constructed it initially.
039 * <p>
040 * A {@link Work} instance must have an id, which is used for equality comparisons and as a key for persistent queues.
041 * <p>
042 * Implementors are strongly advised to inherit from {@link AbstractWork}.
043 *
044 * @see AbstractWork
045 * @since 5.6
046 */
047public interface Work extends Serializable {
048
049    /**
050     * The running state of a {@link Work} instance.
051     * <p>
052     * The following transitions between states are possible:
053     * <ul>
054     * <li>UNKNOWN -> SCHEDULED</li> (unknown from the manager point of view)
055     * <li>SCHEDULED -> CANCELED (is never scheduled or run)
056     * <li>SCHEDULED -> RUNNING
057     * <li>RUNNING -> COMPLETED
058     * <li>RUNNING -> FAILED
059     * <li>RUNNING -> SCHEDULED (is suspended and persisted)
060     * </ul>
061     */
062    enum State {
063        /**
064         * Work instance is not currently referenced in work manager
065         *
066         * @since 5.9.4
067         */
068        UNKNOWN,
069        /**
070         * Work instance is scheduled to run later.
071         */
072        SCHEDULED,
073        /**
074         * Work instance was canceled.
075         * <p>
076         * This happens if:
077         * <ul>
078         * <li>it is never scheduled because another instance with the same id was already scheduled or running (when
079         * scheduled with {@link Scheduling#IF_NOT_SCHEDULED}, {@link Scheduling#IF_NOT_RUNNING}, or
080         * {@link Scheduling#IF_NOT_RUNNING_OR_SCHEDULED}),
081         * <li>it is never run because it was scheduled after commit and the transaction rolled back,
082         * <li>it is never run because it was scheduled, but another work with the same id was scheduled with
083         * {@link Scheduling#CANCEL_SCHEDULED}.
084         * </ul>
085         */
086        CANCELED,
087        /**
088         * Work instance is running.
089         */
090        RUNNING,
091        /**
092         * Work instance has completed normally.
093         */
094        COMPLETED,
095        /**
096         * Work instance has completed with an exception.
097         */
098        FAILED,
099    }
100
101    /**
102     * A progress report about a work instance.
103     * <p>
104     * Progress can be expressed as a percentage, or with a current and total count.
105     * <ul>
106     * <li>26.2% (percent not indeterminate)</li>
107     * <li>12/345 (current not indeterminate)</li>
108     * <li>?/345 (percent and current indeterminate but total non-zero)</li>
109     * <li>? (percent and current indeterminate and total zero)</li>
110     * </ul>
111     *
112     * @since 5.6
113     */
114    public static class Progress implements Serializable {
115
116        private static final long serialVersionUID = 1L;
117
118        public static long CURRENT_INDETERMINATE = -1;
119
120        public static float PERCENT_INDETERMINATE = -1F;
121
122        public static final Progress PROGRESS_INDETERMINATE = new Progress(PERCENT_INDETERMINATE);
123
124        public static final Progress PROGRESS_0_PC = new Progress(0F);
125
126        public static final Progress PROGRESS_100_PC = new Progress(100F);
127
128        protected final float percent;
129
130        protected final long current;
131
132        protected final long total;
133
134        /**
135         * Constructs a {@link Progress} as a percentage.
136         *
137         * @param percent the percentage, a float between 0 and 100, or {@link #PERCENT_INDETERMINATE}
138         */
139        public Progress(float percent) {
140            this.percent = percent > 100F ? 100F : percent;
141            current = CURRENT_INDETERMINATE;
142            total = 0;
143        }
144
145        /**
146         * Constructs a {@link Progress} as a current and total count.
147         *
148         * @param current the current count or {@link #CURRENT_INDETERMINATE}
149         * @param total the total count
150         */
151        public Progress(long current, long total) {
152            percent = PERCENT_INDETERMINATE;
153            this.current = current;
154            this.total = total;
155        }
156
157        public float getPercent() {
158            return percent;
159        }
160
161        public long getCurrent() {
162            return current;
163        }
164
165        public long getTotal() {
166            return total;
167        }
168
169        public boolean getIsWithPercent() {
170            return percent != PERCENT_INDETERMINATE;
171        }
172
173        public boolean getIsWithCurrentAndTotal() {
174            return current != CURRENT_INDETERMINATE;
175        }
176
177        public boolean getIsIndeterminate() {
178            return percent == PERCENT_INDETERMINATE && current == CURRENT_INDETERMINATE;
179        }
180
181        @Override
182        public String toString() {
183            return getClass().getSimpleName() + "(" + (percent == PERCENT_INDETERMINATE ? "?" : Float.valueOf(percent))
184                    + "%, " + (current == CURRENT_INDETERMINATE ? "?" : Long.valueOf(current)) + "/" + total + ")";
185        }
186    }
187
188    /**
189     * Runs the work instance and does all the transaction management and retry.
190     * <p>
191     * Usually only implemented by {@link AbstractWork}, which should be subclassed instead of implementing {@link #run}.
192     */
193    void run();
194
195    /**
196     * This method should implement the actual work done by the {@link Work} instance.
197     * <p>
198     * It should periodically update its progress through {@link #setProgress}.
199     * <p>
200     * To allow for suspension by the {@link WorkManager}, it should periodically call {@link #isSuspending}, and if
201     * {@code true} call {@link #suspended} return early with saved state data.
202     * <p>
203     * Clean up can by implemented by {@link #cleanUp()}.
204     *
205     * @see #isSuspending
206     * @see #suspended
207     * @see #cleanUp
208     */
209    void work();
210
211    /**
212     * The work id.
213     * <p>
214     * The id is used for equality comparisons, and as a key in persistent queues.
215     *
216     * @return the work id, which must not be {@code null}
217     * @since 5.8
218     */
219    String getId();
220
221    /**
222     * This method is called after {@link #work} is done, in a finally block, whether work completed normally or was in
223     * error or was interrupted.
224     *
225     * @param ok {@code true} if the work completed normally
226     * @param e the exception, if available
227     */
228    void cleanUp(boolean ok, Exception e);
229
230    /**
231     * CALLED BY THE WORK MANAGER (not user code) when it requests that this work instance be suspended.
232     *
233     * @since 5.8
234     */
235    void setWorkInstanceSuspending();
236
237    /**
238     * Checks if a suspend has been requested for this work instance by the work manager.
239     * <p>
240     * If {@code true}, then state should be saved, {@link #suspended()} should be called, and the {@link #work()}
241     * method should return.
242     *
243     * @since 5.8
244     */
245    boolean isSuspending();
246
247    /**
248     * Must be called by {@link Work} implementations to advertise that state saving is done, when
249     * {@link #isSuspending()} returned {@code true}. After this is called, the {@link #work()} method should return.
250     *
251     * @since 5.8
252     */
253    void suspended();
254
255    /**
256     * CALLED BY THE WORK MANAGER (not user code) to check if this work instance really suspended.
257     *
258     * @since 5.8
259     */
260    boolean isWorkInstanceSuspended();
261
262    /**
263     * CALLED BY THE WORK MANAGER (not user code) to set this work instance's state.
264     *
265     * @since 5.8
266     */
267    void setWorkInstanceState(State state);
268
269    /**
270     * CALLED BY THE WORK MANAGER (not user code) to get this work instance's state.
271     * <p>
272     * Used only to get the final state of a completed instance ( {@link State#COMPLETED}, {@link State#FAILED} or
273     * {@link State#CANCELED}).
274     *
275     * @since 5.8
276     */
277    State getWorkInstanceState();
278
279    /**
280     * DO NOT USE THIS - gets the state of this work instance.
281     * <p>
282     * This should not be used because for non in-memory persistence, the work instance gets serialized and deserialized
283     * for running and when retrieved after completion, and therefore the original instance cannot get updated after the
284     * original scheduling.
285     *
286     * @return the state
287     * @deprecated since 5.8, use {@link WorkManager#getWorkState} instead
288     */
289    @Deprecated
290    State getState();
291
292    /**
293     * Gets the category for this work.
294     * <p>
295     * Used to choose an execution queue.
296     *
297     * @return the category, or {@code null} for the default
298     */
299    String getCategory();
300
301    /**
302     * Gets a human-readable name for this work instance.
303     *
304     * @return a human-readable name
305     */
306    String getTitle();
307
308    /**
309     * Gets a human-readable status for this work instance.
310     *
311     * @return a human-readable status
312     */
313    String getStatus();
314
315    /**
316     * Gets the time at which this work instance was first scheduled.
317     *
318     * @return the scheduling time (milliseconds since epoch)
319     */
320    long getSchedulingTime();
321
322    /**
323     * Gets the time at which this work instance was first started.
324     *
325     * @return the start time (milliseconds since epoch), or {@code 0} if not stated
326     */
327    long getStartTime();
328
329    /**
330     * Gets the time at which this work instance was completed, suspended or failed.
331     *
332     * @return the completion time (milliseconds since epoch), or {@code 0} if not completed
333     */
334    long getCompletionTime();
335
336    /**
337     * CALLED BY THE WORK MANAGER (not user code) to set the start time on the work instance.
338     *
339     * @since 5.9.2
340     */
341    void setStartTime();
342
343    /**
344     * This method should be called periodically by the actual work method when it knows of its progress.
345     *
346     * @param progress the progress
347     * @see Progress#Progress(float)
348     * @see Progress#Progress(long, long)
349     */
350    void setProgress(Progress progress);
351
352    /**
353     * Gets a progress report for this work instance.
354     *
355     * @return a progress report, not {@code null}
356     */
357    Progress getProgress();
358
359    /**
360     * Gets the user on behalf of which this work is done.
361     * <p>
362     * This is informative only.
363     *
364     * @return the user id, or {@code null}
365     */
366    String getUserId();
367
368    /**
369     * Gets the document impacted by the work.
370     * <p>
371     * Returns {@code null} if the work isn't about a single document.
372     *
373     * @return the document, or {@code null}. This is always a {@link DocumentLocation} with an {@link IdRef}
374     * @since 5.8
375     */
376    DocumentLocation getDocument();
377
378    /**
379     * Gets the documents impacted by the work.
380     * <p>
381     * Returns {@code null} if the work isn't about documents.
382     *
383     * @return the documents, or an empty list. List elements are always a {@link DocumentLocation} with an
384     *         {@link IdRef}
385     * @since 5.8
386     */
387    List<DocumentLocation> getDocuments();
388
389    /**
390     * Returns {@code true} if {@link #getDocument} is only the root of a set of documents on which this Work instance
391     * will act.
392     *
393     * @return {@code true} if a whole tree is impacted
394     * @since 5.8
395     */
396    boolean isDocumentTree();
397
398    /**
399     * Returns the schedule path
400     *
401     * @since 5.8
402     */
403    WorkSchedulePath getSchedulePath();
404
405    /**
406     * Set the schedule path, internal usage
407     *
408     * @since 5.8
409     */
410    void setSchedulePath(WorkSchedulePath path);
411
412    /**
413     * CALLED BY THE WORK MANAGER (not user code) to get this work instance's result.
414     *
415     * @since 7.4
416     */
417    String getWorkInstanceResult();
418}