001/* 002 * (C) Copyright 2012-2016 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 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 */ 173 void run(); 174 175 /** 176 * This method should implement the actual work done by the {@link Work} instance. 177 * <p> 178 * It should periodically update its progress through {@link #setProgress}. 179 * <p> 180 * To allow for suspension by the {@link WorkManager}, it should periodically call {@link #isSuspending}, and if 181 * {@code true} call {@link #suspended} return early with saved state data. 182 * <p> 183 * Clean up can by implemented by {@link #cleanUp()}. 184 * 185 * @see #isSuspending 186 * @see #suspended 187 * @see #cleanUp 188 */ 189 void work(); 190 191 /** 192 * The work id. 193 * <p> 194 * The id is used for equality comparisons, and as a key in persistent queues. 195 * 196 * @return the work id, which must not be {@code null} 197 * @since 5.8 198 */ 199 String getId(); 200 201 /** 202 * This method is called after {@link #work} is done, in a finally block, whether work completed normally or was in 203 * error or was interrupted. 204 * 205 * @param ok {@code true} if the work completed normally 206 * @param e the exception, if available 207 */ 208 void cleanUp(boolean ok, Exception e); 209 210 /** 211 * CALLED BY THE WORK MANAGER (not user code) when it requests that this work instance be suspended. 212 * 213 * @since 5.8 214 */ 215 void setWorkInstanceSuspending(); 216 217 /** 218 * Checks if a suspend has been requested for this work instance by the work manager. 219 * <p> 220 * If {@code true}, then state should be saved, {@link #suspended()} should be called, and the {@link #work()} 221 * method should return. 222 * 223 * @since 5.8 224 */ 225 boolean isSuspending(); 226 227 /** 228 * Must be called by {@link Work} implementations to advertise that state saving is done, when 229 * {@link #isSuspending()} returned {@code true}. After this is called, the {@link #work()} method should return. 230 * 231 * @since 5.8 232 */ 233 void suspended(); 234 235 /** 236 * CALLED BY THE WORK MANAGER (not user code) to check if this work instance really suspended. 237 * 238 * @since 5.8 239 */ 240 boolean isWorkInstanceSuspended(); 241 242 /** 243 * CALLED BY THE WORK MANAGER (not user code) to set this work instance's state. 244 * 245 * @since 5.8 246 */ 247 void setWorkInstanceState(State state); 248 249 /** 250 * CALLED BY THE WORK MANAGER (not user code) to get this work instance's state. 251 * <p> 252 * Used only to get the final state of a completed instance ( {@link State#COMPLETED}, {@link State#FAILED} or 253 * {@link State#CANCELED}). 254 * 255 * @since 5.8 256 */ 257 State getWorkInstanceState(); 258 259 /** 260 * Gets the category for this work. 261 * <p> 262 * Used to choose an execution queue. 263 * 264 * @return the category, or {@code null} for the default 265 */ 266 String getCategory(); 267 268 /** 269 * Gets a human-readable name for this work instance. 270 * 271 * @return a human-readable name 272 */ 273 String getTitle(); 274 275 /** 276 * Gets a human-readable status for this work instance. 277 * 278 * @return a human-readable status 279 */ 280 String getStatus(); 281 282 /** 283 * Gets the time at which this work instance was first scheduled. 284 * 285 * @return the scheduling time (milliseconds since epoch) 286 */ 287 long getSchedulingTime(); 288 289 /** 290 * Gets the time at which this work instance was first started. 291 * 292 * @return the start time (milliseconds since epoch), or {@code 0} if not stated 293 */ 294 long getStartTime(); 295 296 /** 297 * Gets the time at which this work instance was completed, suspended or failed. 298 * 299 * @return the completion time (milliseconds since epoch), or {@code 0} if not completed 300 */ 301 long getCompletionTime(); 302 303 /** 304 * CALLED BY THE WORK MANAGER (not user code) to set the start time on the work instance. 305 * 306 * @since 5.9.2 307 */ 308 void setStartTime(); 309 310 /** 311 * This method should be called periodically by the actual work method when it knows of its progress. 312 * 313 * @param progress the progress 314 * @see Progress#Progress(float) 315 * @see Progress#Progress(long, long) 316 */ 317 void setProgress(Progress progress); 318 319 /** 320 * Gets a progress report for this work instance. 321 * 322 * @return a progress report, not {@code null} 323 */ 324 Progress getProgress(); 325 326 /** 327 * Gets the user on behalf of which this work is done. 328 * 329 * @return the originating username, or {@code null} 330 * @since 8.1 331 */ 332 String getOriginatingUsername(); 333 334 /** 335 * Gets the document impacted by the work. 336 * <p> 337 * Returns {@code null} if the work isn't about a single document. 338 * 339 * @return the document, or {@code null}. This is always a {@link DocumentLocation} with an {@link IdRef} 340 * @since 5.8 341 */ 342 DocumentLocation getDocument(); 343 344 /** 345 * Gets the documents impacted by the work. 346 * <p> 347 * Returns {@code null} if the work isn't about documents. 348 * 349 * @return the documents, or an empty list. List elements are always a {@link DocumentLocation} with an 350 * {@link IdRef} 351 * @since 5.8 352 */ 353 List<DocumentLocation> getDocuments(); 354 355 /** 356 * Returns {@code true} if {@link #getDocument} is only the root of a set of documents on which this Work instance 357 * will act. 358 * 359 * @return {@code true} if a whole tree is impacted 360 * @since 5.8 361 */ 362 boolean isDocumentTree(); 363 364 /** 365 * Returns the schedule path 366 * 367 * @since 5.8 368 */ 369 WorkSchedulePath getSchedulePath(); 370 371 /** 372 * Set the schedule path, internal usage 373 * 374 * @since 5.8 375 */ 376 void setSchedulePath(WorkSchedulePath path); 377 378}