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