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}