1 2 package cz.cuni.amis.pogamut.base.agent.navigation.impl; 3 4 import java.util.List; 5 import java.util.logging.Level; 6 import java.util.logging.Logger; 7 8 import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutor; 9 import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutorHelper; 10 import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutorState; 11 import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture; 12 import cz.cuni.amis.pogamut.base.agent.navigation.IStuckDetector; 13 import cz.cuni.amis.pogamut.base.agent.navigation.PathExecutorState; 14 import cz.cuni.amis.utils.exception.PogamutException; 15 import cz.cuni.amis.utils.future.FutureStatus; 16 import cz.cuni.amis.utils.future.FutureWithListeners; 17 import cz.cuni.amis.utils.future.IFutureListener; 18 19 /** 20 * BasePathExecutor provides a stub implementation of abstract methods of the {@link AbstractPathExecutor} 21 * which correctly sets the path executor states along the way and provide methods for reporting failures. 22 * <p><p> 23 * Note that it is somewhat hard to use this stub-implementation as the base for implementing 24 * your own {@link IPathExecutor} implementation - the {@link AbstractPathExecutor} might be more suitable. 25 * That's because this implementation is defining a way how its fields are used, how methods should be synchronized, 26 * how they are called (method protocol), etc. 27 * <p><p> 28 * Implementation notes: 29 * <ul> 30 * <li> 31 * As the {@link IPathExecutorState} may have different implementation providing different kind of the information 32 * about the path - {@link BasePathExecutor} defines {@link BasePathExecutor#createState(PathExecutorState)} as a hook 33 * for custom {@link IPathExecutorState} implementation (currently it uses simple {@link BasePathExecutorState}). 34 * </li> 35 * <li> 36 * Many methods here are made final - this might seem to block your inventions, but it is actually for your own good. 37 * If you find yourself in need to override some method that is made final, it is advised that you switch to {@link AbstractPathExecutor} 38 * or copy-paste the {@link BasePathExecutor} code and do your changes in copied code. 39 * </li> 40 * <li> 41 * There are several important final methods that drive the {@link BasePathExecutor}. 42 * <ul> 43 * <li>{@link BasePathExecutor#followPath(IPathFuture)} - interface public method / meant to be called from the outside - begin following the path / handles path future listener</li> 44 * <li>{@link BasePathExecutor#pathComputed()} - protected method / it is automatically called by the {@link BasePathExecutor} whenever the path future returns the path</li> 45 * <li>{@link BasePathExecutor#pathComputationFailed()} - protected method / it is automatically called by the {@link BasePathExecutor} whenever path future reports {@link FutureStatus#CANCELED} or {@link FutureStatus#COMPUTATION_EXCEPTION}. 46 * <li>{@link BasePathExecutor#switchToAnotherPathElement(int)} - protected method / meant to be called from within the path executor (descendant) whenever the executor should start to navigate towards another path element</li> 47 * <li>{@link BasePathExecutor#stuck(IStuckDetector)} - protected method / automatically called by {@link BasePathExecutor#executePath()} whenever one of the {@link AbstractPathExecutor#stuckDetectors} reports stuck</li> 48 * <li>{@link BasePathExecutor#stop()} - interface public method / meant to be called both from the outside or from the inside of path executor / stops the path executor</li> 49 * </ul> 50 * </li> 51 * <li> 52 * Important final methods from the previous paragraph are usually split into three parts 53 * <ol> 54 * <li>their actual implementation that can't be overridden, it (usually) changes the state of the path executor and recalls pre/post methods</li> 55 * <li>pre-phase method that is called (from within the method's implementation) just before the state of the executor is changed</li> 56 * <li>post-phase method that is called (from within the method's implementation) just after the state of the executor is changed</li> 57 * </ol> 58 * Pre/Post-phase methods are usually abstract (if not, they contain meaningful implementation but that can be overriden if wished for) and are meant to be implemented in descendants. 59 * They allow you to fine control the state of the executor before and after the change of its state without the need to program boring part of the code that changes the state in a correct way. 60 * Think of implementing the descendant of this class as implementing reactions to the changes of executor's state. 61 * </li> 62 * <li> 63 * Additionally, there are methods that are not called from anywhere of {@link BasePathExecutor}. These are: 64 * <ul> 65 * <li>{@link BasePathExecutor#switchToAnotherPathElement(int)} - this method should be called either from {@link BasePathExecutor#prePathComputedImpl()} or {@link BasePathExecutor#pathComputed()} to mark 66 * the index of the path element that is going to be pursued first (this index will likely be '0').</li> 67 * <li>{@link BasePathExecutor#stuck(IStuckDetector)} - use this method to report that the stuck has been detected</li> 68 * <li>{@link BasePathExecutor#checkStuckDetectors()} 69 * </ul> 70 * </li> 71 * <li> 72 * It is up to the descendant how the path execution will be actually implemented (either by some event-driven model, i.e., 73 * reacting on the events that will come from the environment, or spawning some thread that will regularly perform some operation, etc.). 74 * </li> 75 * </ul> 76 * 77 * @author Jimmy 78 */ 79 public abstract class BasePathExecutor<PATH_ELEMENT> extends AbstractPathExecutor<PATH_ELEMENT> implements IPathExecutorHelper<PATH_ELEMENT> { 80 81 /** 82 * Mutex object synchronizing access to {@link BasePathExecutor#followPath(IPathFuture)} and 83 * {@link BasePathExecutor#stop()} methods. 84 */ 85 protected Object mutex = new Object(); 86 87 /** 88 * Current path future of the path executor. Path future is set in {@link BasePathExecutor#followPath(IPathFuture)} 89 * and removed (set to 'null') in {@link BasePathExecutor#stop()}. 90 */ 91 protected IPathFuture<PATH_ELEMENT> pathFuture = null; 92 93 /** 94 * Marks the index of the previous path element (path element that has been previously pursued) 95 * from the path element list of {@link BasePathExecutor#getPath()}. 96 * <p><p> 97 * Setting value to this field manually must be done only inside {@link BasePathExecutor#preSwitchToAnotherPathElementImpl(int)} 98 * and {@link BasePathExecutor#switchToAnotherPathElementImpl()}. 99 */ 100 protected int previousPathElementIndex = -1; 101 102 /** 103 * Marks the index of the current path element (path element that is currently pursued) 104 * from the path element list of {@link BasePathExecutor#getPath()}. 105 * <p><p> 106 * Setting value to this field manually must be done only inside {@link BasePathExecutor#preSwitchToAnotherPathElementImpl(int)} 107 * and {@link BasePathExecutor#switchToAnotherPathElementImpl()}. 108 */ 109 protected int pathElementIndex = -1; 110 111 /** 112 * {@link BasePathExecutor#pathFuture} listener that recalls methods {@link BasePathExecutor#pathComputed()} 113 * or {@link BasePathExecutor#pathComputationFailed()} based upon the change of the future status. Note that 114 * the listener recalls these methods only if the event comes from the {@link BasePathExecutor#pathFuture} to prevent 115 * wrong invocations. 116 * <p><p> 117 * If you wish to use different implementation of the {@link IFutureListener}, reinstantiate this field 118 * in the constructor of your own descendant. 119 */ 120 IFutureListener<List<PATH_ELEMENT>> pathFutureListener = new IFutureListener<List<PATH_ELEMENT>>() { 121 122 @Override 123 public void futureEvent(FutureWithListeners<List<PATH_ELEMENT>> source, FutureStatus oldStatus, FutureStatus newStatus) { 124 synchronized(mutex) { 125 source.removeFutureListener(this); 126 if (pathFuture != source) { 127 // we've been called from the future that is not being used by the executor 128 return; 129 } 130 switch(newStatus) { 131 case FUTURE_IS_READY: 132 pathComputed(); 133 return; 134 case COMPUTATION_EXCEPTION: 135 case CANCELED: 136 pathComputationFailed(); 137 return; 138 case FUTURE_IS_BEING_COMPUTED: 139 throw new RuntimeException("FutureWithListeners can't change its state to FUTURE_IS_BEING_COMPUTED."); 140 } 141 } 142 } 143 144 }; 145 146 public BasePathExecutor() { 147 this(null); 148 } 149 150 public BasePathExecutor(Logger log) { 151 this.log = log; 152 } 153 154 // @Override 155 @Override 156 public int getPathElementIndex() { 157 return pathElementIndex; 158 } 159 160 @Override 161 public IPathFuture<PATH_ELEMENT> getPathFuture() { 162 return pathFuture; 163 } 164 165 @Override 166 public PATH_ELEMENT getPathFrom() { 167 return getPathFuture().getPathFrom(); 168 } 169 170 @Override 171 public PATH_ELEMENT getPathTo() { 172 return getPathFuture().getPathTo(); 173 } 174 175 176 /** 177 * Utility method that is responsible for creating new state for the path executor. You may override this method 178 * to fine-control which implementations {@link IPathExecutorState} are instantiated. 179 * <p><p> 180 * This method allows you to provide own {@link IPathExecutorState} implementation that may carry additional 181 * information about the executor's state. 182 * 183 * @param state 184 * @return 185 */ 186 protected IPathExecutorState createState(PathExecutorState state) { 187 switch(state) { 188 case SWITCHED_TO_ANOTHER_PATH_ELEMENT: return new BasePathExecutorState(PathExecutorState.SWITCHED_TO_ANOTHER_PATH_ELEMENT); 189 default: return BasePathExecutorState.getState(state); 190 } 191 } 192 193 // --- 194 // ===== 195 // followPath() 196 // ===== 197 // --- 198 199 /** 200 * Tell the executor to start navigating the agent along the 'path'. 201 * <p><p> 202 * If called and the {@link BasePathExecutor#isExecuting()}, it first calls {@link BasePathExecutor#stop()}. 203 * <p><p> 204 * For more info see {@link AbstractPathExecutor#followPath(IPathFuture)} 205 * 206 * @param path path to navigate along 207 */ 208 @Override 209 public final void followPath(IPathFuture<? extends PATH_ELEMENT> path) { 210 synchronized(mutex) { 211 if (isExecuting()) { 212 stop(); 213 } 214 if (log != null && log.isLoggable(Level.INFO)) log.info("followPath called, destination " + path.getPathTo()); 215 pathFuture = (IPathFuture<PATH_ELEMENT>) path; 216 preFollowPathImpl(); 217 switchState(createState(PathExecutorState.FOLLOW_PATH_CALLED)); 218 followPathImpl(); 219 if (path == null) { 220 pathComputationFailed(); 221 return; 222 } 223 switch(path.getStatus()) { 224 case COMPUTATION_EXCEPTION: 225 case CANCELED: 226 pathComputationFailed(); 227 return; 228 case FUTURE_IS_READY: 229 if (getPath() == null) { 230 pathComputationFailed(); 231 } else { 232 pathComputed(); 233 } 234 return; 235 case FUTURE_IS_BEING_COMPUTED: 236 pathFuture.addFutureListener(pathFutureListener); 237 break; 238 default: 239 throw new RuntimeException("Unhandled path future status '" + path.getStatus() + "'."); 240 } 241 // if we get here, we've add a future listener to 'pathFuture' 242 // but what if the path future status has changed before we have attached the listener? 243 // ... check it again :-) 244 switch(path.getStatus()) { 245 case COMPUTATION_EXCEPTION: 246 case CANCELED: 247 pathComputationFailed(); 248 return; 249 case FUTURE_IS_READY: 250 pathComputed(); 251 return; 252 case FUTURE_IS_BEING_COMPUTED: 253 return; 254 default: 255 throw new RuntimeException("Unhandled path future status '" + path.getStatus() + "'."); 256 } 257 } 258 } 259 260 /** 261 * Method that is called just before the executor's state is switched to {@link PathExecutorState#FOLLOW_PATH_CALLED} from within 262 * the {@link BasePathExecutor#followPath(IPathFuture)} method. 263 * <p><p> 264 * You may utilize this methods this way: 265 * <ul> 266 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 267 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 268 * </ul> 269 * <p> 270 * Current implementation ensures that the state of the path executor is cleared. 271 */ 272 protected void preFollowPathImpl() { 273 // ensure that the previous state is cleared 274 previousPathElementIndex = -1; 275 pathElementIndex = -1; 276 } 277 278 /** 279 * Method that is called just after the executor's state is switched to {@link PathExecutorState#FOLLOW_PATH_CALLED} from 280 * within the {@link BasePathExecutor#followPath(IPathFuture)} method. 281 * <p><p> 282 * You may utilize this method this way: 283 * <ul> 284 * <li>Examine the state of the executor / values produced by executor state listeners.</li> 285 * </ul> 286 */ 287 protected abstract void followPathImpl(); 288 289 // --- 290 // ===== 291 // pathComputed() 292 // ===== 293 // --- 294 295 /** 296 * Path has been computed and is available in {@link BasePathExecutor#pathFuture}. This method is automatically called 297 * from the {@link BasePathExecutor#followPath(IPathFuture)} or by {@link BasePathExecutor#pathFutureListener} whenever 298 * the path is ready. 299 * <p><p> 300 * Effective only if the state is: {@link PathExecutorState#FOLLOW_PATH_CALLED} 301 */ 302 protected final void pathComputed() { 303 synchronized(mutex) { 304 if (notInState(PathExecutorState.FOLLOW_PATH_CALLED)) return; 305 if (log != null && log.isLoggable(Level.FINE)) log.fine("path computed, size == " + getPath().size()); 306 pathFuture.removeFutureListener(pathFutureListener); 307 prePathComputedImpl(); 308 switchState(createState(PathExecutorState.PATH_COMPUTED)); 309 if (inState(PathExecutorState.PATH_COMPUTED)) { 310 pathComputedImpl(); 311 } 312 } 313 } 314 315 /** 316 * Method that is called just before the executor's state is switched to {@link PathExecutorState#PATH_COMPUTED} from within 317 * the {@link BasePathExecutor#pathComputed()} method. Note that since this method is called, the path can be simply 318 * obtained by calling {@link BasePathExecutor#getPath()}. 319 * <p><p> 320 * You may utilize this method (for instance) to: 321 * <ul> 322 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 323 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 324 * </ul> 325 * <p> 326 * Note that you should call {@link BasePathExecutor#switchToAnotherPathElement(int)} (to mark the index of the first path element) 327 * in this method or in {@link BasePathExecutor#pathComputedImpl()}. The first path element index will likely be '0'. 328 * <p><p> 329 * Resets and enables all {@link AbstractPathExecutor#stuckDetectors}. 330 */ 331 protected void prePathComputedImpl() { 332 for (IStuckDetector detector : stuckDetectors) { 333 detector.reset(); 334 detector.setEnabled(true); 335 } 336 } 337 338 /** 339 * Method that is called just after the executor's state is switched to {@link PathExecutorState#PATH_COMPUTED} from 340 * within the {@link BasePathExecutor#pathComputed()} method. Note that the path can be simply obtained 341 * by calling {@link BasePathExecutor#getPath()}. 342 * <p><p> 343 * You may utilize this method (for instance) to: 344 * <ul> 345 * <li>Examine the state of the executor / values produced by executor state listeners.</li> 346 * </ul> 347 * <p> 348 * Note that you should call {@link BasePathExecutor#switchToAnotherPathElement(int)} (to mark the index of the first path element) 349 * in this method or in {@link BasePathExecutor#prePathComputedImpl()}. The first path element index will likely be '0'. 350 */ 351 protected abstract void pathComputedImpl(); 352 353 // --- 354 // ===== 355 // pathComputationFailed() 356 // ===== 357 // --- 358 359 /** 360 * Path computation has failed, path is unavailable and the executor can't start navigate the agent through the environment. 361 * This method is automatically called 362 * from the {@link BasePathExecutor#followPath(IPathFuture)} or by {@link BasePathExecutor#pathFutureListener} whenever 363 * it is found out that the path can never be obtained from the {@link BasePathExecutor#pathFuture}. 364 * <p><p> 365 * Effective only if the state is: {@link PathExecutorState#FOLLOW_PATH_CALLED} 366 */ 367 protected final void pathComputationFailed() { 368 synchronized(mutex) { 369 if (notInState(PathExecutorState.FOLLOW_PATH_CALLED)) return; 370 if (log != null && log.isLoggable(Level.WARNING)) log.warning("path computation failed"); 371 if (pathFuture != null) { 372 pathFuture.removeFutureListener(pathFutureListener); 373 } 374 prePathComputationFailed(); 375 switchState(createState(PathExecutorState.PATH_COMPUTATION_FAILED)); 376 if (inState(PathExecutorState.PATH_COMPUTATION_FAILED)) { 377 pathComputationFailedImpl(); 378 } 379 } 380 } 381 382 /** 383 * Method that is called just before the executor's state is switched to {@link PathExecutorState#PATH_COMPUTATION_FAILED} from within 384 * the {@link BasePathExecutor#pathComputationFailed()} method. 385 * <p><p> 386 * You may utilize this methods (for instance) to: 387 * <ul> 388 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 389 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 390 * <li>Clean up some executor's fields.</li> 391 * </ul> 392 * <p> 393 * Empty implementation, does not doing anything. 394 */ 395 protected void prePathComputationFailed() { 396 } 397 398 /** 399 * Method that is called just after the executor's state is switched to {@link PathExecutorState#PATH_COMPUTATION_FAILED} from 400 * within the {@link BasePathExecutor#pathComputationFailed()} method. Note that the path can be simply obtained 401 * by calling {@link BasePathExecutor#getPath()}. 402 * <p><p> 403 * You may utilize this method (for instance) to: 404 * <ul> 405 * <li>Examine the state of the executor / values produced by executor state listeners.</li> 406 * <li>Clean up some executor's fields.</li> 407 * </ul> 408 */ 409 protected abstract void pathComputationFailedImpl(); 410 411 // --- 412 // ===== 413 // switchToAnotherPathElement() 414 // ===== 415 // --- 416 417 /** 418 * Switches from current path element index into the new one. You have to call this method 419 * from your implementation whenever you want to change {@link BasePathExecutor#pathElementIndex}. 420 * <p><p> 421 * This method should be also called as a reaction to {@link BasePathExecutor#pathComputed()} method call, i.e., 422 * from {@link BasePathExecutor#prePathComputedImpl()} or {@link BasePathExecutor#pathComputedImpl()} to change 423 * the index into '0' (or other index as you see fit). 424 * <p><p> 425 * Effective only if {@link AbstractPathExecutor#isExecuting()}. 426 */ 427 @Override 428 public final void switchToAnotherPathElement(int index) { 429 synchronized(mutex) { 430 if (!isExecuting()) return; 431 List<PATH_ELEMENT> path = getPath(); 432 if (path == null) throw new PogamutException("Can't switch to element of index '" + index + "' as the current path executor's path is null.", this); 433 if (index < 0 || index >= path.size()) throw new PogamutException("Can't switch to element of index '" + index + "' as it is out of path range (path.size() = " + path.size() + ").", this); 434 if (log != null && log.isLoggable(Level.FINER)) log.finer("switching to path element " + (index+1) + "/" + getPath().size() + " -> " + path.get(index)); 435 preSwitchToAnotherPathElementImpl(index); 436 switchState(createState(PathExecutorState.SWITCHED_TO_ANOTHER_PATH_ELEMENT)); 437 if (inState(PathExecutorState.SWITCHED_TO_ANOTHER_PATH_ELEMENT)) { 438 switchToAnotherPathElementImpl(); 439 } 440 } 441 } 442 443 /** 444 * Method that is called just before the executor's state is switched to {@link PathExecutorState#SWITCHED_TO_ANOTHER_PATH_ELEMENT} from within 445 * the {@link BasePathExecutor#switchToAnotherPathElement(int)} method. Note that this method is called to alter 446 * values inside {@link BasePathExecutor#previousPathElementIndex} and {@link BasePathExecutor#pathElementIndex} 447 * by using 'newIndex'. 448 * <p><p> 449 * You may additionally utilize this method (for instance) to: 450 * <ul> 451 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 452 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 453 * </ul> 454 * <p> 455 * Current implementation does (and nothing else): 456 * <ul> 457 * <li>previousPathElementIndex = pathElementIndex;</li> 458 * <li>pathElementIndex = newIndex;</li> 459 * </ul> 460 * 461 * @param newIndex index of the path element that should be pursued now 462 */ 463 protected void preSwitchToAnotherPathElementImpl(int newIndex) { 464 previousPathElementIndex = pathElementIndex; 465 pathElementIndex = newIndex; 466 } 467 468 /** 469 * Method that is called just after the executor's state is switched to {@link PathExecutorState#SWITCHED_TO_ANOTHER_PATH_ELEMENT} from 470 * within the {@link BasePathExecutor#switchToAnotherPathElement(int)} method. Note that this method 471 * is called after the values inside {@link BasePathExecutor#previousPathElementIndex} and {@link BasePathExecutor#pathElementIndex} 472 * are overwritten with new ones. 473 * <p><p> 474 * You may utilize this method (for instance) to: 475 * <ul> 476 * <li>Examine the state of the executor / values produced by executor state listeners.</li> 477 * </ul> 478 */ 479 protected abstract void switchToAnotherPathElementImpl(); 480 481 // --- 482 // ===== 483 // stop() 484 // ===== 485 // --- 486 487 /** 488 * Used to stop the path executor, for more info see {@link AbstractPathExecutor#stop()}. 489 * <p><p> 490 * Effective only if the state is <b>NOT</b>: {@link PathExecutorState#STOPPED} 491 */ 492 @Override 493 public final void stop() { 494 synchronized(mutex) { 495 if (inState(PathExecutorState.STOPPED)) return; 496 if (log != null && log.isLoggable(Level.INFO)) log.info("stop"); 497 stopImpl(); 498 switchState(createState(PathExecutorState.STOPPED)); 499 if (inState(PathExecutorState.STOPPED)) { 500 stopped(); 501 } 502 } 503 } 504 505 /** 506 * Method that is called just before the executor's state is switched to {@link PathExecutorState#STOPPED} from within the 507 * {@link BasePathExecutor#stop()} method. Note that this method is called to clean up internal data structures 508 * before we switch itself into {@link PathExecutorState#STOPPED} state. 509 * <p><p> 510 * You may additionally utilize this method (for instance) to: 511 * <ul> 512 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 513 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 514 * </ul> 515 * <p> 516 * Current implementation does (and nothing else): 517 * <ul> 518 * <li>sets {@link BasePathExecutor#previousPathElementIndex} and {@link BasePathExecutor#pathElementIndex} to -1</li> 519 * <li>removes {@link BasePathExecutor#pathFutureListener} from the {@link BasePathExecutor#pathFuture}</li> 520 * <li>sets {@link BasePathExecutor#pathFuture} to null</li> 521 * <li>disables all {@link AbstractPathExecutor#stuckDetectors}</li> 522 * </ul> 523 * 524 * @param newIndex index of the path element that should be pursued now 525 */ 526 protected void stopImpl() { 527 previousPathElementIndex = -1; 528 pathElementIndex = -1; 529 if (pathFuture != null) { 530 pathFuture.removeFutureListener(pathFutureListener); 531 pathFuture = null; 532 } 533 for (IStuckDetector stuckDetector : stuckDetectors) { 534 stuckDetector.setEnabled(false); 535 } 536 } 537 538 /** 539 * Method that is called just after the executor's state is switched to {@link PathExecutorState#STOPPED} from 540 * within the {@link BasePathExecutor#stop()} method. 541 * <p><p> 542 * You may utilize this method (for instance) to: 543 * <ul> 544 * <li>Finish the internal data structures clean up.</li> 545 * </ul> 546 */ 547 protected abstract void stopped(); 548 549 // --- 550 // ===== 551 // checkStuckDetectors() 552 // ===== 553 // --- 554 555 /** 556 * This method checks (one-by-one) stuck detectors whether some of them is reporting that the agent has stuck. 557 * If the stuck is detected, particular {@link IStuckDetector} is returned. If the stuck is not detected, 558 * null is returned. 559 * 560 * @return first detector to report that agent has stuck or null 561 */ 562 @Override 563 public IStuckDetector checkStuckDetectors() { 564 for (IStuckDetector detector : stuckDetectors) { 565 if (detector.isStuck()) { 566 return detector; 567 } 568 } 569 return null; 570 } 571 572 // --- 573 // ===== 574 // stuck() 575 // ===== 576 // --- 577 578 /** 579 * Method that changes the state to {@link PathExecutorState#STUCK} that should be called whenever some 580 * stuck detector detects that the agent is stuck. 581 * <p><p> 582 * It is currently called only from {@link BasePathExecutor#checkStuckDetectors()} which must be called from 583 * the descendant. 584 * <p><p> 585 * Note that you may actually pass 'null' as 'detector' into the method. 586 * <p><p> 587 * Effective only if the state is: {@link PathExecutorState#FOLLOW_PATH_CALLED} or {@link PathExecutorState#PATH_COMPUTED} or {@link PathExecutorState#SWITCHED_TO_ANOTHER_PATH_ELEMENT} 588 */ 589 @Override 590 public final void stuck() { 591 synchronized(mutex) { 592 if (notInState(PathExecutorState.FOLLOW_PATH_CALLED, PathExecutorState.PATH_COMPUTED, PathExecutorState.SWITCHED_TO_ANOTHER_PATH_ELEMENT)) return; 593 preStuckImpl(); 594 switchState(createState(PathExecutorState.STUCK)); 595 if (inState(PathExecutorState.STUCK)) { 596 stuckImpl(); 597 } 598 } 599 } 600 601 /** 602 * Method that is called just before the executor's state is switched to {@link PathExecutorState#STUCKD} from within the 603 * {@link BasePathExecutor#stuck(IStuckDetector)} method. Note that this method is called to clean up internal data structures 604 * before we switch itself into {@link PathExecutorState#STUCK} state. 605 * <p><p> 606 * BasePathExecutor's implementation disables all {@link BasePathExecutor#stuckDetectors}. 607 * <p><p> 608 * You may utilize this method (for instance) to: 609 * <ul> 610 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 611 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 612 * </ul> 613 * <p><p> 614 * Current implementation does (and nothing else): 615 * <ul> 616 * <li>disables all {@link AbstractPathExecutor#stuckDetectors}</li> 617 * </ul> 618 */ 619 protected void preStuckImpl() { 620 for (IStuckDetector stuckDetector : stuckDetectors) { 621 stuckDetector.setEnabled(false); 622 } 623 } 624 625 /** 626 * Method that is called just after the executor's state is switched to {@link PathExecutorState#STUCK} from 627 * within the {@link BasePathExecutor#stuck(IStuckDetector)} method. 628 * <p><p> 629 * You may utilize this method (for instance) to: 630 * <ul> 631 * <li>Finish the internal data structures clean up.</li> 632 * </ul> 633 * <p><p> 634 * <b>WARNING:</b> 'null' may be passed as 'detector' if the stuck has been detected by different component 635 */ 636 protected abstract void stuckImpl(); 637 638 // --- 639 // ===== 640 // targetReached() 641 // ===== 642 // --- 643 644 /** 645 * Method that should be called whenever the path executor reaches the end of the path. Currently this method 646 * is not called from anywhere of {@link BasePathExecutor}. 647 * <p><p> 648 * Effective only if the state is: {@link PathExecutorState#FOLLOW_PATH_CALLED} or {@link PathExecutorState#PATH_COMPUTED} or {@link PathExecutorState#SWITCHED_TO_ANOTHER_PATH_ELEMENT} 649 */ 650 public final void targetReached() { 651 synchronized(mutex) { 652 if (notInState(PathExecutorState.FOLLOW_PATH_CALLED, PathExecutorState.PATH_COMPUTED, PathExecutorState.SWITCHED_TO_ANOTHER_PATH_ELEMENT)) return; 653 preTargetReachedImpl(); 654 switchState(createState(PathExecutorState.TARGET_REACHED)); 655 if (inState(PathExecutorState.TARGET_REACHED)) { 656 targetReachedImpl(); 657 } 658 } 659 } 660 661 /** 662 * Method that is called just before the executor's state is switched to {@link PathExecutorState#TARGET_REACHED} from within the 663 * {@link BasePathExecutor#targetReached()} method. 664 * <p><p> 665 * You may utilize this method (for instance) to: 666 * <ul> 667 * <li>Initialize inner/public fields that can be used by executor state listeners.</li> 668 * <li>Store values that should be passed into {@link BasePathExecutor#createState(PathExecutorState)} to create more-informed state object.</li> 669 * <li>Clean up some internal data structures as the target has been reached.</li> 670 * </ul> 671 * <p> 672 * Empty implementation, does not doing anything. 673 */ 674 protected void preTargetReachedImpl() { 675 } 676 677 /** 678 * Method that is called just after the executor's state is switched to {@link PathExecutorState#TARGET_REACHED} from 679 * within the {@link BasePathExecutor#targetReached()} method. 680 * <p><p> 681 * You may utilize this method (for instance) to: 682 * <ul> 683 * <li>Examine the state of the executor / values produced by executor state listeners.</li> 684 * <li>Clean up some internal data structures as the target has been reached.</li> 685 * </ul> 686 */ 687 protected abstract void targetReachedImpl(); 688 689 }