001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.backends.task; 018 019import static org.opends.messages.BackendMessages.*; 020import static org.opends.server.config.ConfigConstants.*; 021import static org.opends.server.util.CollectionUtils.*; 022import static org.opends.server.util.ServerConstants.*; 023import static org.opends.server.util.StaticUtils.*; 024 025import java.io.File; 026import java.io.IOException; 027import java.util.GregorianCalendar; 028import java.util.HashMap; 029import java.util.Iterator; 030import java.util.LinkedHashMap; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Map; 034import java.util.TreeSet; 035import java.util.concurrent.TimeUnit; 036import java.util.concurrent.locks.ReentrantLock; 037 038import org.forgerock.i18n.LocalizableMessage; 039import org.forgerock.i18n.slf4j.LocalizedLogger; 040import org.forgerock.opendj.ldap.ByteString; 041import org.forgerock.opendj.ldap.DN; 042import org.forgerock.opendj.ldap.ResultCode; 043import org.forgerock.opendj.ldap.schema.AttributeType; 044import org.opends.server.api.AlertGenerator; 045import org.opends.server.api.DirectoryThread; 046import org.opends.server.core.DirectoryServer; 047import org.opends.server.core.SearchOperation; 048import org.opends.server.core.ServerContext; 049import org.opends.server.types.Attribute; 050import org.opends.server.types.Attributes; 051import org.opends.server.types.DirectoryException; 052import org.opends.server.types.Entry; 053import org.opends.server.types.ExistingFileBehavior; 054import org.opends.server.types.InitializationException; 055import org.opends.server.types.LDIFExportConfig; 056import org.opends.server.types.LDIFImportConfig; 057import org.opends.server.types.LockManager.DNLock; 058import org.opends.server.types.Operation; 059import org.opends.server.types.SearchFilter; 060import org.opends.server.util.LDIFException; 061import org.opends.server.util.LDIFReader; 062import org.opends.server.util.LDIFWriter; 063import org.opends.server.util.TimeThread; 064 065/** 066 * This class defines a task scheduler for the Directory Server that will 067 * control the execution of scheduled tasks and other administrative functions 068 * that need to occur on a regular basis. 069 */ 070public class TaskScheduler 071 extends DirectoryThread 072 implements AlertGenerator 073{ 074 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 075 076 /** 077 * The fully-qualified name of this class. 078 */ 079 private static final String CLASS_NAME = 080 "org.opends.server.backends.task.TaskScheduler"; 081 082 083 084 /** 085 * The maximum length of time in milliseconds to sleep between iterations 086 * through the scheduler loop. 087 */ 088 private static long MAX_SLEEP_TIME = 5000; 089 090 091 /** Indicates whether the scheduler is currently running. */ 092 private boolean isRunning; 093 /** Indicates whether a request has been received to stop the scheduler. */ 094 private boolean stopRequested; 095 096 /** The entry that serves as the immediate parent for recurring tasks. */ 097 private Entry recurringTaskParentEntry; 098 /** The entry that serves as the immediate parent for scheduled tasks. */ 099 private Entry scheduledTaskParentEntry; 100 /** The top-level entry at the root of the task tree. */ 101 private Entry taskRootEntry; 102 103 /** The set of recurring tasks defined in the server. */ 104 private final HashMap<String,RecurringTask> recurringTasks = new HashMap<>(); 105 /** The set of tasks associated with this scheduler. */ 106 private final HashMap<String,Task> tasks = new HashMap<>(); 107 /** The set of worker threads that are actively busy processing tasks. */ 108 private final HashMap<String,TaskThread> activeThreads = new HashMap<>(); 109 110 /** The thread ID for the next task thread to be created;. */ 111 private int nextThreadID = 1; 112 113 /** The set of worker threads that may be used to process tasks. */ 114 private final LinkedList<TaskThread> idleThreads = new LinkedList<>(); 115 116 /** The lock used to provide threadsafe access to the scheduler. */ 117 private final ReentrantLock schedulerLock = new ReentrantLock(); 118 119 /** The task backend with which this scheduler is associated. */ 120 private TaskBackend taskBackend; 121 122 /** The thread being used to actually run the scheduler. */ 123 private Thread schedulerThread; 124 125 /** The set of recently-completed tasks that need to be retained. */ 126 private final TreeSet<Task> completedTasks = new TreeSet<>(); 127 /** The set of tasks that have been scheduled but not yet arrived. */ 128 private final TreeSet<Task> pendingTasks = new TreeSet<>(); 129 /** The set of tasks that are currently running. */ 130 private final TreeSet<Task> runningTasks = new TreeSet<>(); 131 132 private ServerContext serverContext; 133 134 /** 135 * Creates a new task scheduler that will be used to ensure that tasks are 136 * invoked at the appropriate times. 137 * @param serverContext 138 * The server context 139 * @param taskBackend The task backend with which this scheduler is 140 * associated. 141 * 142 * @throws InitializationException If a problem occurs while initializing 143 * the scheduler from the backing file. 144 */ 145 public TaskScheduler(ServerContext serverContext, TaskBackend taskBackend) 146 throws InitializationException 147 { 148 super("Task Scheduler Thread"); 149 150 this.serverContext = serverContext; 151 this.taskBackend = taskBackend; 152 153 DirectoryServer.registerAlertGenerator(this); 154 155 initializeTasksFromBackingFile(); 156 157 for (RecurringTask recurringTask : recurringTasks.values()) { 158 Task task = null; 159 try { 160 task = recurringTask.scheduleNextIteration(new GregorianCalendar()); 161 } catch (DirectoryException de) { 162 logger.error(de.getMessageObject()); 163 } 164 if (task != null) { 165 try { 166 scheduleTask(task, false); 167 } catch (DirectoryException de) { 168 // This task might have been already scheduled from before 169 // and thus got initialized from backing file, otherwise 170 // log error and continue. 171 if (de.getResultCode() != ResultCode.ENTRY_ALREADY_EXISTS) { 172 logger.error(de.getMessageObject()); 173 } 174 } 175 } 176 } 177 } 178 179 180 181 /** 182 * Adds a recurring task to the scheduler, optionally scheduling the first 183 * iteration for processing. 184 * 185 * @param recurringTask The recurring task to add to the scheduler. 186 * @param scheduleIteration Indicates whether to schedule an iteration of 187 * the recurring task. 188 * 189 * @throws DirectoryException If a problem occurs while trying to add the 190 * recurring task (e.g., there's already another 191 * recurring task defined with the same ID). 192 */ 193 public void addRecurringTask(RecurringTask recurringTask, 194 boolean scheduleIteration) 195 throws DirectoryException 196 { 197 schedulerLock.lock(); 198 199 try 200 { 201 String id = recurringTask.getRecurringTaskID(); 202 203 if (recurringTasks.containsKey(id)) 204 { 205 LocalizableMessage message = 206 ERR_TASKSCHED_DUPLICATE_RECURRING_ID.get(id); 207 throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message); 208 } 209 210 Attribute attr = Attributes.create(ATTR_TASK_STATE, TaskState.RECURRING.toString()); 211 Entry recurringTaskEntry = recurringTask.getRecurringTaskEntry(); 212 recurringTaskEntry.putAttribute(attr.getAttributeDescription().getAttributeType(), newArrayList(attr)); 213 214 if (scheduleIteration) 215 { 216 Task task = recurringTask.scheduleNextIteration( 217 new GregorianCalendar()); 218 if (task != null) 219 { 220 // If there is an existing task with the same id 221 // and it is in completed state, take its place. 222 Task t = tasks.get(task.getTaskID()); 223 if (t != null && TaskState.isDone(t.getTaskState())) 224 { 225 removeCompletedTask(t.getTaskID()); 226 } 227 228 scheduleTask(task, false); 229 } 230 } 231 232 recurringTasks.put(id, recurringTask); 233 writeState(); 234 } 235 finally 236 { 237 schedulerLock.unlock(); 238 } 239 } 240 241 242 243 /** 244 * Removes the recurring task with the given ID. 245 * 246 * @param recurringTaskID The ID of the recurring task to remove. 247 * 248 * @return The recurring task that was removed, or <CODE>null</CODE> if there 249 * was no such recurring task. 250 * 251 * @throws DirectoryException If there is currently a pending or running 252 * iteration of the associated recurring task. 253 */ 254 public RecurringTask removeRecurringTask(String recurringTaskID) 255 throws DirectoryException 256 { 257 schedulerLock.lock(); 258 259 try 260 { 261 RecurringTask recurringTask = recurringTasks.remove(recurringTaskID); 262 HashMap<String,Task> iterationsMap = new HashMap<>(); 263 264 for (Task t : tasks.values()) 265 { 266 // Find any existing task iterations and try to cancel them. 267 if (t.getRecurringTaskID() != null && 268 t.getRecurringTaskID().equals(recurringTaskID)) 269 { 270 TaskState state = t.getTaskState(); 271 if (!TaskState.isDone(state) && !TaskState.isCancelled(state)) 272 { 273 cancelTask(t.getTaskID()); 274 } 275 iterationsMap.put(t.getTaskID(), t); 276 } 277 } 278 279 // Remove any completed task iterations. 280 for (Map.Entry<String,Task> iterationEntry : iterationsMap.entrySet()) 281 { 282 if (TaskState.isDone(iterationEntry.getValue().getTaskState())) 283 { 284 removeCompletedTask(iterationEntry.getKey()); 285 } 286 } 287 288 writeState(); 289 return recurringTask; 290 } 291 finally 292 { 293 schedulerLock.unlock(); 294 } 295 } 296 297 298 299 /** 300 * Schedules the provided task for execution. If the scheduler is active and 301 * the start time has arrived, then the task will begin execution immediately. 302 * Otherwise, it will be placed in the pending queue to be started at the 303 * appropriate time. 304 * 305 * @param task The task to be scheduled. 306 * @param writeState Indicates whether the current state information for 307 * the scheduler should be persisted to disk once the 308 * task is scheduled. 309 * 310 * @throws DirectoryException If a problem occurs while trying to schedule 311 * the task (e.g., there's already another task 312 * defined with the same ID). 313 */ 314 public void scheduleTask(Task task, boolean writeState) 315 throws DirectoryException 316 { 317 schedulerLock.lock(); 318 319 320 try 321 { 322 String id = task.getTaskID(); 323 324 if (tasks.containsKey(id)) 325 { 326 LocalizableMessage message = ERR_TASKSCHED_DUPLICATE_TASK_ID.get(id); 327 throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message); 328 } 329 330 for (String dependencyID : task.getDependencyIDs()) 331 { 332 Task t = tasks.get(dependencyID); 333 if (t == null) 334 { 335 LocalizableMessage message = ERR_TASKSCHED_DEPENDENCY_MISSING.get(id, dependencyID); 336 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 337 } 338 } 339 340 tasks.put(id, task); 341 342 TaskState state = shouldStart(task); 343 task.setTaskState(state); 344 345 if (state == TaskState.RUNNING) 346 { 347 TaskThread taskThread; 348 if (idleThreads.isEmpty()) 349 { 350 taskThread = new TaskThread(this, nextThreadID++); 351 taskThread.start(); 352 } 353 else 354 { 355 taskThread = idleThreads.removeFirst(); 356 } 357 358 runningTasks.add(task); 359 activeThreads.put(task.getTaskID(), taskThread); 360 taskThread.setTask(task); 361 } 362 else if (TaskState.isDone(state)) 363 { 364 if (state == TaskState.CANCELED_BEFORE_STARTING && task.isRecurring()) 365 { 366 pendingTasks.add(task); 367 } 368 else 369 { 370 completedTasks.add(task); 371 } 372 } 373 else 374 { 375 pendingTasks.add(task); 376 } 377 378 if (writeState) 379 { 380 writeState(); 381 } 382 } 383 finally 384 { 385 schedulerLock.unlock(); 386 } 387 } 388 389 390 391 /** 392 * Attempts to cancel the task with the given task ID. This will only cancel 393 * the task if it has not yet started running. If it has started, then it 394 * will not be interrupted. 395 * 396 * @param taskID The task ID of the task to cancel. 397 * 398 * @return The requested task, which may or may not have actually been 399 * cancelled (the task state should make it possible to determine 400 * whether it was cancelled), or <CODE>null</CODE> if there is no 401 * such task. 402 */ 403 public Task cancelTask(String taskID) 404 { 405 schedulerLock.lock(); 406 407 try 408 { 409 Task t = tasks.get(taskID); 410 if (t == null) 411 { 412 return null; 413 } 414 415 if (TaskState.isPending(t.getTaskState())) 416 { 417 pendingTasks.remove(t); 418 t.setTaskState(TaskState.CANCELED_BEFORE_STARTING); 419 addCompletedTask(t); 420 writeState(); 421 } 422 423 return t; 424 } 425 finally 426 { 427 schedulerLock.unlock(); 428 } 429 } 430 431 432 433 /** 434 * Removes the specified pending task. It will be completely removed rather 435 * than moving it to the set of completed tasks. 436 * 437 * @param taskID The task ID of the pending task to remove. 438 * 439 * @return The task that was removed. 440 * 441 * @throws DirectoryException If the requested task is not in the pending 442 * queue. 443 */ 444 public Task removePendingTask(String taskID) 445 throws DirectoryException 446 { 447 schedulerLock.lock(); 448 449 try 450 { 451 Task t = tasks.get(taskID); 452 if (t == null) 453 { 454 LocalizableMessage message = ERR_TASKSCHED_REMOVE_PENDING_NO_SUCH_TASK.get(taskID); 455 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 456 } 457 458 if (TaskState.isPending(t.getTaskState())) 459 { 460 tasks.remove(taskID); 461 pendingTasks.remove(t); 462 writeState(); 463 return t; 464 } 465 else 466 { 467 LocalizableMessage message = ERR_TASKSCHED_REMOVE_PENDING_NOT_PENDING.get(taskID); 468 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 469 } 470 } 471 finally 472 { 473 schedulerLock.unlock(); 474 } 475 } 476 477 478 479 /** 480 * Removes the specified completed task. 481 * 482 * @param taskID The task ID of the completed task to remove. 483 * 484 * @return The task that was removed. 485 * 486 * @throws DirectoryException If the requested task could not be found. 487 */ 488 public Task removeCompletedTask(String taskID) 489 throws DirectoryException 490 { 491 schedulerLock.lock(); 492 493 try 494 { 495 Iterator<Task> iterator = completedTasks.iterator(); 496 while (iterator.hasNext()) 497 { 498 Task t = iterator.next(); 499 if (t.getTaskID().equals(taskID)) 500 { 501 iterator.remove(); 502 tasks.remove(taskID); 503 writeState(); 504 return t; 505 } 506 } 507 508 LocalizableMessage message = ERR_TASKSCHED_REMOVE_COMPLETED_NO_SUCH_TASK.get(taskID); 509 throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); 510 } 511 finally 512 { 513 schedulerLock.unlock(); 514 } 515 } 516 517 518 519 /** 520 * Indicates that processing has completed on the provided task thread and 521 * that it is now available for processing other tasks. The thread may be 522 * immediately used for processing another task if appropriate. 523 * 524 * @param taskThread The thread that has completed processing on its 525 * previously-assigned task. 526 * @param completedTask The task for which processing has been completed. 527 * @param taskState The task state for this completed task. 528 * 529 * @return <CODE>true</CODE> if the thread should continue running and 530 * wait for the next task to process, or <CODE>false</CODE> if it 531 * should exit immediately. 532 */ 533 public boolean threadDone(TaskThread taskThread, Task completedTask, 534 TaskState taskState) 535 { 536 schedulerLock.lock(); 537 538 try 539 { 540 completedTask.setCompletionTime(TimeThread.getTime()); 541 completedTask.setTaskState(taskState); 542 addCompletedTask(completedTask); 543 544 try 545 { 546 completedTask.sendNotificationEMailMessage(); 547 } 548 catch (Exception e) 549 { 550 logger.traceException(e); 551 } 552 553 String taskID = completedTask.getTaskID(); 554 if (activeThreads.remove(taskID) == null) 555 { 556 return false; 557 } 558 559 // See if the task is part of a recurring task. 560 // If so, then schedule the next iteration. 561 scheduleNextRecurringTaskIteration(completedTask, 562 new GregorianCalendar()); 563 writeState(); 564 565 if (isRunning) 566 { 567 idleThreads.add(taskThread); 568 return true; 569 } 570 else 571 { 572 return false; 573 } 574 } 575 finally 576 { 577 schedulerLock.unlock(); 578 } 579 } 580 581 582 583 /** 584 * Check if a given task is a recurring task iteration and re-schedule it. 585 * @param completedTask The task for which processing has been completed. 586 * @param calendar The calendar date and time to schedule from. 587 */ 588 protected void scheduleNextRecurringTaskIteration(Task completedTask, 589 GregorianCalendar calendar) 590 { 591 String recurringTaskID = completedTask.getRecurringTaskID(); 592 if (recurringTaskID != null) 593 { 594 RecurringTask recurringTask = recurringTasks.get(recurringTaskID); 595 if (recurringTask != null) 596 { 597 Task newIteration = null; 598 try { 599 newIteration = recurringTask.scheduleNextIteration(calendar); 600 } catch (DirectoryException de) { 601 logger.error(de.getMessageObject()); 602 } 603 if (newIteration != null) 604 { 605 try 606 { 607 // If there is an existing task with the same id 608 // and it is in completed state, take its place. 609 Task t = tasks.get(newIteration.getTaskID()); 610 if (t != null && TaskState.isDone(t.getTaskState())) 611 { 612 removeCompletedTask(t.getTaskID()); 613 } 614 615 scheduleTask(newIteration, false); 616 } 617 catch (DirectoryException de) 618 { 619 // This task might have been already scheduled from before 620 // and thus got initialized from backing file, otherwise 621 // log error and continue. 622 if (de.getResultCode() != ResultCode.ENTRY_ALREADY_EXISTS) 623 { 624 logger.traceException(de); 625 626 LocalizableMessage message = 627 ERR_TASKSCHED_ERROR_SCHEDULING_RECURRING_ITERATION.get( 628 recurringTaskID, de.getMessageObject()); 629 logger.error(message); 630 631 DirectoryServer.sendAlertNotification(this, 632 ALERT_TYPE_CANNOT_SCHEDULE_RECURRING_ITERATION, 633 message); 634 } 635 } 636 } 637 } 638 } 639 } 640 641 642 643 /** 644 * Adds the provided task to the set of completed tasks associated with the 645 * scheduler. It will be automatically removed after the appropriate 646 * retention time has elapsed. 647 * 648 * @param completedTask The task for which processing has completed. 649 */ 650 public void addCompletedTask(Task completedTask) 651 { 652 // The scheduler lock is reentrant, so even if we already hold it, we can 653 // acquire it again. 654 schedulerLock.lock(); 655 656 try 657 { 658 completedTasks.add(completedTask); 659 runningTasks.remove(completedTask); 660 661 // If the task never ran set its completion 662 // time here explicitly so that it can be 663 // correctly evaluated for retention later. 664 if (completedTask.getCompletionTime() == -1) 665 { 666 completedTask.setCompletionTime(TimeThread.getTime()); 667 } 668 } 669 finally 670 { 671 schedulerLock.unlock(); 672 } 673 } 674 675 676 677 /** 678 * Stops the scheduler so that it will not start any scheduled tasks. It will 679 * not attempt to interrupt any tasks that are already running. Note that 680 * once the scheduler has been stopped, it cannot be restarted and it will be 681 * necessary to restart the task backend to start a new scheduler instance. 682 */ 683 public void stopScheduler() 684 { 685 stopRequested = true; 686 687 try 688 { 689 schedulerThread.interrupt(); 690 } 691 catch (Exception e) 692 { 693 logger.traceException(e); 694 } 695 696 try 697 { 698 schedulerThread.join(); 699 } 700 catch (Exception e) 701 { 702 logger.traceException(e); 703 } 704 705 pendingTasks.clear(); 706 runningTasks.clear(); 707 completedTasks.clear(); 708 tasks.clear(); 709 710 for (TaskThread thread : idleThreads) 711 { 712 LocalizableMessage message = INFO_TASKBE_INTERRUPTED_BY_SHUTDOWN.get(); 713 thread.interruptTask(TaskState.STOPPED_BY_SHUTDOWN, message, true); 714 } 715 } 716 717 718 719 /** 720 * Attempts to interrupt any tasks that are actively running. This will not 721 * make any attempt to stop the scheduler. 722 * 723 * @param interruptState The state that should be assigned to the tasks if 724 * they are successfully interrupted. 725 * @param interruptReason A message indicating the reason that the tasks 726 * are to be interrupted. 727 * @param waitForStop Indicates whether this method should wait until 728 * all active tasks have stopped before returning. 729 */ 730 public void interruptRunningTasks(TaskState interruptState, 731 LocalizableMessage interruptReason, 732 boolean waitForStop) 733 { 734 // Grab a copy of the running threads so that we can operate on them without 735 // holding the lock. 736 LinkedList<TaskThread> threadList = new LinkedList<>(); 737 738 schedulerLock.lock(); 739 try 740 { 741 threadList.addAll(activeThreads.values()); 742 } 743 finally 744 { 745 schedulerLock.unlock(); 746 } 747 748 749 // Iterate through all the task threads and request that they stop 750 // processing. 751 for (TaskThread t : threadList) 752 { 753 try 754 { 755 t.interruptTask(interruptState, interruptReason, true); 756 } 757 catch (Exception e) 758 { 759 logger.traceException(e); 760 } 761 } 762 763 764 // If we should actually wait for all the task threads to stop, then do so. 765 if (waitForStop) 766 { 767 for (TaskThread t : threadList) 768 { 769 try 770 { 771 t.join(); 772 } 773 catch (Exception e) 774 { 775 logger.traceException(e); 776 } 777 } 778 } 779 } 780 781 782 783 /** 784 * Operates in a loop, launching tasks at the appropriate time and performing 785 * any necessary periodic cleanup. 786 */ 787 @Override 788 public void run() 789 { 790 isRunning = true; 791 schedulerThread = currentThread(); 792 793 try 794 { 795 while (! stopRequested) 796 { 797 schedulerLock.lock(); 798 799 boolean writeState = false; 800 long sleepTime = MAX_SLEEP_TIME; 801 802 try 803 { 804 // If there are any pending tasks that need to be started, then do so 805 // now. 806 Iterator<Task> iterator = pendingTasks.iterator(); 807 while (iterator.hasNext()) 808 { 809 Task t = iterator.next(); 810 TaskState state = shouldStart(t); 811 812 if (state == TaskState.RUNNING) 813 { 814 TaskThread taskThread; 815 if (idleThreads.isEmpty()) 816 { 817 taskThread = new TaskThread(this, nextThreadID++); 818 taskThread.start(); 819 } 820 else 821 { 822 taskThread = idleThreads.removeFirst(); 823 } 824 825 runningTasks.add(t); 826 activeThreads.put(t.getTaskID(), taskThread); 827 taskThread.setTask(t); 828 829 iterator.remove(); 830 writeState = true; 831 } 832 else if (state == TaskState.WAITING_ON_START_TIME) 833 { 834 // If we're waiting for the start time to arrive, then see if that 835 // will come before the next sleep time is up. 836 long waitTime = t.getScheduledStartTime() - TimeThread.getTime(); 837 sleepTime = Math.min(sleepTime, waitTime); 838 } 839 // Recurring task iteration has to spawn the next one 840 // even if the current iteration has been canceled. 841 else if (state == TaskState.CANCELED_BEFORE_STARTING && t.isRecurring()) 842 { 843 if (t.getScheduledStartTime() > TimeThread.getTime()) { 844 // If we're waiting for the start time to arrive, 845 // then see if that will come before the next sleep time is up. 846 long waitTime = 847 t.getScheduledStartTime() - TimeThread.getTime(); 848 sleepTime = Math.min(sleepTime, waitTime); 849 } else { 850 TaskThread taskThread; 851 if (idleThreads.isEmpty()) { 852 taskThread = new TaskThread(this, nextThreadID++); 853 taskThread.start(); 854 } else { 855 taskThread = idleThreads.removeFirst(); 856 } 857 runningTasks.add(t); 858 activeThreads.put(t.getTaskID(), taskThread); 859 taskThread.setTask(t); 860 } 861 } 862 863 if (state != t.getTaskState()) 864 { 865 t.setTaskState(state); 866 writeState = true; 867 } 868 } 869 870 871 // Clean up any completed tasks that have been around long enough. 872 long retentionTimeMillis = 873 TimeUnit.SECONDS.toMillis(taskBackend.getRetentionTime()); 874 long oldestRetainedCompletionTime = 875 TimeThread.getTime() - retentionTimeMillis; 876 iterator = completedTasks.iterator(); 877 while (iterator.hasNext()) 878 { 879 Task t = iterator.next(); 880 if (t.getCompletionTime() < oldestRetainedCompletionTime) 881 { 882 iterator.remove(); 883 tasks.remove(t.getTaskID()); 884 writeState = true; 885 } 886 } 887 888 // If anything changed, then make sure that the on-disk state gets 889 // updated. 890 if (writeState) 891 { 892 writeState(); 893 } 894 } 895 finally 896 { 897 schedulerLock.unlock(); 898 } 899 900 901 try 902 { 903 if (sleepTime > 0) 904 { 905 Thread.sleep(sleepTime); 906 } 907 } catch (InterruptedException ie) {} 908 } 909 } 910 finally 911 { 912 isRunning = false; 913 } 914 } 915 916 917 918 /** 919 * Determines whether the specified task should start running. This is based 920 * on the start time, the set of dependencies, and whether or not the 921 * scheduler is active. Note that the caller to this method must hold the 922 * scheduler lock. 923 * 924 * @param task The task for which to make the determination. 925 * 926 * @return The task state that should be used for the task. It should be 927 * RUNNING if the task should be started, or some other state if not. 928 */ 929 private TaskState shouldStart(Task task) 930 { 931 // If the task has finished we don't want to restart it 932 TaskState state = task.getTaskState(); 933 934 // Reset task state if recurring. 935 if (state == TaskState.RECURRING) { 936 state = null; 937 } 938 939 if (state != null && TaskState.isDone(state)) 940 { 941 return state; 942 } 943 944 if (! isRunning) 945 { 946 return TaskState.UNSCHEDULED; 947 } 948 949 if (task.getScheduledStartTime() > TimeThread.getTime()) 950 { 951 return TaskState.WAITING_ON_START_TIME; 952 } 953 954 LinkedList<String> dependencyIDs = task.getDependencyIDs(); 955 if (dependencyIDs != null) 956 { 957 for (String dependencyID : dependencyIDs) 958 { 959 Task t = tasks.get(dependencyID); 960 if (t != null) 961 { 962 TaskState tState = t.getTaskState(); 963 if (!TaskState.isDone(tState)) 964 { 965 return TaskState.WAITING_ON_DEPENDENCY; 966 } 967 if (!TaskState.isSuccessful(tState)) 968 { 969 FailedDependencyAction action = task.getFailedDependencyAction(); 970 switch (action) 971 { 972 case CANCEL: 973 cancelTask(task.getTaskID()); 974 return task.getTaskState(); 975 case DISABLE: 976 task.setTaskState(TaskState.DISABLED); 977 return task.getTaskState(); 978 default: 979 break; 980 } 981 } 982 } 983 } 984 } 985 986 return TaskState.RUNNING; 987 } 988 989 990 991 /** 992 * Populates the scheduler with information read from the task backing file. 993 * If no backing file is found, then create a new one. The caller must 994 * already hold the scheduler lock or otherwise ensure that this is a 995 * threadsafe operation. 996 * 997 * @throws InitializationException If a fatal error occurs while attempting 998 * to perform the initialization. 999 */ 1000 private void initializeTasksFromBackingFile() 1001 throws InitializationException 1002 { 1003 String backingFilePath = taskBackend.getTaskBackingFile(); 1004 1005 try 1006 { 1007 File backingFile = getFileForPath(backingFilePath); 1008 if (! backingFile.exists()) 1009 { 1010 createNewTaskBackingFile(); 1011 return; 1012 } 1013 1014 1015 LDIFImportConfig importConfig = new LDIFImportConfig(backingFilePath); 1016 LDIFReader ldifReader = new LDIFReader(importConfig); 1017 1018 taskRootEntry = null; 1019 recurringTaskParentEntry = null; 1020 scheduledTaskParentEntry = null; 1021 1022 while (true) 1023 { 1024 Entry entry; 1025 1026 try 1027 { 1028 entry = ldifReader.readEntry(); 1029 } 1030 catch (LDIFException le) 1031 { 1032 logger.traceException(le); 1033 1034 if (le.canContinueReading()) 1035 { 1036 logger.error(ERR_TASKSCHED_CANNOT_PARSE_ENTRY_RECOVERABLE, 1037 backingFilePath, le.getLineNumber(), le.getMessage()); 1038 1039 continue; 1040 } 1041 else 1042 { 1043 try 1044 { 1045 ldifReader.close(); 1046 } 1047 catch (Exception e) 1048 { 1049 logger.traceException(e); 1050 } 1051 1052 LocalizableMessage message = ERR_TASKSCHED_CANNOT_PARSE_ENTRY_FATAL.get( 1053 backingFilePath, le.getLineNumber(), le.getMessage()); 1054 throw new InitializationException(message); 1055 } 1056 } 1057 1058 if (entry == null) 1059 { 1060 break; 1061 } 1062 1063 DN entryDN = entry.getName(); 1064 if (entryDN.equals(taskBackend.getTaskRootDN())) 1065 { 1066 taskRootEntry = entry; 1067 } 1068 else if (entryDN.equals(taskBackend.getRecurringTasksParentDN())) 1069 { 1070 recurringTaskParentEntry = entry; 1071 } 1072 else if (entryDN.equals(taskBackend.getScheduledTasksParentDN())) 1073 { 1074 scheduledTaskParentEntry = entry; 1075 } 1076 else 1077 { 1078 DN parentDN = DirectoryServer.getParentDNInSuffix(entryDN); 1079 if (parentDN == null) 1080 { 1081 logger.error(ERR_TASKSCHED_ENTRY_HAS_NO_PARENT, entryDN, taskBackend.getTaskRootDN()); 1082 } 1083 else if (parentDN.equals(taskBackend.getScheduledTasksParentDN())) 1084 { 1085 try 1086 { 1087 Task task = entryToScheduledTask(entry, null); 1088 if (TaskState.isDone(task.getTaskState())) 1089 { 1090 String id = task.getTaskID(); 1091 if (tasks.containsKey(id)) 1092 { 1093 logger.warn(WARN_TASKSCHED_DUPLICATE_TASK_ID, id); 1094 } 1095 else 1096 { 1097 completedTasks.add(task); 1098 tasks.put(id, task); 1099 } 1100 } 1101 else 1102 { 1103 scheduleTask(task, false); 1104 } 1105 } 1106 catch (DirectoryException de) 1107 { 1108 logger.traceException(de); 1109 logger.error(ERR_TASKSCHED_CANNOT_SCHEDULE_TASK_FROM_ENTRY, entryDN, de.getMessageObject()); 1110 } 1111 } 1112 else if (parentDN.equals(taskBackend.getRecurringTasksParentDN())) 1113 { 1114 try 1115 { 1116 RecurringTask recurringTask = entryToRecurringTask(entry); 1117 addRecurringTask(recurringTask, false); 1118 } 1119 catch (DirectoryException de) 1120 { 1121 logger.traceException(de); 1122 logger.error(ERR_TASKSCHED_CANNOT_SCHEDULE_RECURRING_TASK_FROM_ENTRY, entryDN, de.getMessageObject()); 1123 } 1124 } 1125 else 1126 { 1127 logger.error(ERR_TASKSCHED_INVALID_TASK_ENTRY_DN, entryDN, backingFilePath); 1128 } 1129 } 1130 } 1131 1132 ldifReader.close(); 1133 } 1134 catch (IOException ioe) 1135 { 1136 logger.traceException(ioe); 1137 1138 LocalizableMessage message = ERR_TASKSCHED_ERROR_READING_TASK_BACKING_FILE.get( 1139 backingFilePath, stackTraceToSingleLineString(ioe)); 1140 throw new InitializationException(message, ioe); 1141 } 1142 } 1143 1144 1145 1146 /** 1147 * Creates a new task backing file that contains only the basic structure but 1148 * no scheduled or recurring task entries. The caller must already hold the 1149 * scheduler lock or otherwise ensure that this is a threadsafe operation. 1150 * 1151 * @throws InitializationException If a problem occurs while attempting to 1152 * create the backing file. 1153 */ 1154 private void createNewTaskBackingFile() 1155 throws InitializationException 1156 { 1157 String backingFile = taskBackend.getTaskBackingFile(); 1158 LDIFExportConfig exportConfig = 1159 new LDIFExportConfig(backingFile, ExistingFileBehavior.OVERWRITE); 1160 1161 try 1162 { 1163 LDIFWriter writer = new LDIFWriter(exportConfig); 1164 1165 // First, write a header to the top of the file to indicate that it should 1166 // not be manually edited. 1167 writer.writeComment(INFO_TASKBE_BACKING_FILE_HEADER.get(), 80); 1168 1169 1170 // Next, create the required hierarchical entries and add them to the 1171 // LDIF. 1172 taskRootEntry = createEntry(taskBackend.getTaskRootDN()); 1173 writer.writeEntry(taskRootEntry); 1174 1175 scheduledTaskParentEntry = 1176 createEntry(taskBackend.getScheduledTasksParentDN()); 1177 writer.writeEntry(scheduledTaskParentEntry); 1178 1179 recurringTaskParentEntry = 1180 createEntry(taskBackend.getRecurringTasksParentDN()); 1181 writer.writeEntry(recurringTaskParentEntry); 1182 1183 1184 // Close the file and we're done. 1185 writer.close(); 1186 } 1187 catch (IOException ioe) 1188 { 1189 logger.traceException(ioe); 1190 1191 LocalizableMessage message = ERR_TASKSCHED_CANNOT_CREATE_BACKING_FILE.get( 1192 backingFile, stackTraceToSingleLineString(ioe)); 1193 throw new InitializationException(message, ioe); 1194 } 1195 catch (LDIFException le) 1196 { 1197 logger.traceException(le); 1198 LocalizableMessage message = ERR_TASKSCHED_CANNOT_CREATE_BACKING_FILE.get( 1199 backingFile, le.getMessage()); 1200 throw new InitializationException(message, le); 1201 } 1202 } 1203 1204 1205 1206 /** 1207 * Writes state information about all tasks and recurring tasks to disk. 1208 */ 1209 public void writeState() 1210 { 1211 String backingFilePath = taskBackend.getTaskBackingFile(); 1212 String tmpFilePath = backingFilePath + ".tmp"; 1213 LDIFExportConfig exportConfig = 1214 new LDIFExportConfig(tmpFilePath, ExistingFileBehavior.OVERWRITE); 1215 1216 1217 schedulerLock.lock(); 1218 1219 try 1220 { 1221 LDIFWriter writer = new LDIFWriter(exportConfig); 1222 1223 // First, write a header to the top of the file to indicate that it should 1224 // not be manually edited. 1225 writer.writeComment(INFO_TASKBE_BACKING_FILE_HEADER.get(), 80); 1226 1227 1228 // Next, write the structural entries to the top of the LDIF. 1229 writer.writeEntry(taskRootEntry); 1230 writer.writeEntry(scheduledTaskParentEntry); 1231 writer.writeEntry(recurringTaskParentEntry); 1232 1233 1234 // Iterate through all the recurring tasks and write them to LDIF. 1235 for (RecurringTask recurringTask : recurringTasks.values()) 1236 { 1237 writer.writeEntry(recurringTask.getRecurringTaskEntry()); 1238 } 1239 1240 1241 // Iterate through all the scheduled tasks and write them to LDIF. 1242 for (Task task : tasks.values()) 1243 { 1244 writer.writeEntry(task.getTaskEntry()); 1245 } 1246 1247 1248 // Close the file. 1249 writer.close(); 1250 1251 1252 // See if there is a ".save" file. If so, then delete it. 1253 File saveFile = getFileForPath(backingFilePath + ".save"); 1254 try 1255 { 1256 if (saveFile.exists()) 1257 { 1258 saveFile.delete(); 1259 } 1260 } 1261 catch (Exception e) 1262 { 1263 logger.traceException(e); 1264 } 1265 1266 1267 // If there is an existing backing file, then rename it to ".save". 1268 File backingFile = getFileForPath(backingFilePath); 1269 try 1270 { 1271 if (backingFile.exists()) 1272 { 1273 backingFile.renameTo(saveFile); 1274 } 1275 } 1276 catch (Exception e) 1277 { 1278 logger.traceException(e); 1279 1280 LocalizableMessage message = WARN_TASKSCHED_CANNOT_RENAME_CURRENT_BACKING_FILE.get( 1281 backingFilePath, saveFile.getAbsolutePath(), stackTraceToSingleLineString(e)); 1282 logger.warn(message); 1283 DirectoryServer.sendAlertNotification( 1284 this, ALERT_TYPE_CANNOT_RENAME_CURRENT_TASK_FILE, message); 1285 } 1286 1287 1288 // Rename the ".tmp" file into place. 1289 File tmpFile = getFileForPath(tmpFilePath); 1290 try 1291 { 1292 tmpFile.renameTo(backingFile); 1293 } 1294 catch (Exception e) 1295 { 1296 logger.traceException(e); 1297 1298 LocalizableMessage message = ERR_TASKSCHED_CANNOT_RENAME_NEW_BACKING_FILE.get( 1299 tmpFilePath, backingFilePath, stackTraceToSingleLineString(e)); 1300 logger.error(message); 1301 DirectoryServer.sendAlertNotification( 1302 this, ALERT_TYPE_CANNOT_RENAME_NEW_TASK_FILE, message); 1303 } 1304 } 1305 catch (LDIFException le) 1306 { 1307 logger.traceException(le); 1308 LocalizableMessage message = 1309 ERR_TASKSCHED_CANNOT_WRITE_BACKING_FILE.get(tmpFilePath, le 1310 .getMessage()); 1311 logger.error(message); 1312 DirectoryServer.sendAlertNotification(this, 1313 ALERT_TYPE_CANNOT_WRITE_TASK_FILE, message); 1314 } 1315 catch (Exception e) 1316 { 1317 logger.traceException(e); 1318 LocalizableMessage message = 1319 ERR_TASKSCHED_CANNOT_WRITE_BACKING_FILE.get(tmpFilePath, 1320 stackTraceToSingleLineString(e)); 1321 logger.error(message); 1322 DirectoryServer.sendAlertNotification(this, 1323 ALERT_TYPE_CANNOT_WRITE_TASK_FILE, message); 1324 } 1325 finally 1326 { 1327 schedulerLock.unlock(); 1328 } 1329 } 1330 1331 1332 1333 /** 1334 * Retrieves the total number of entries in the task backend. 1335 * 1336 * @return The total number of entries in the task backend. 1337 */ 1338 public long getEntryCount() 1339 { 1340 schedulerLock.lock(); 1341 1342 try 1343 { 1344 return tasks.size() + recurringTasks.size() + 3; 1345 } 1346 finally 1347 { 1348 schedulerLock.unlock(); 1349 } 1350 } 1351 1352 /** 1353 * Retrieves the number of scheduled tasks in the task backend. 1354 * 1355 * @return The total number of entries in the task backend. 1356 */ 1357 public long getScheduledTaskCount() 1358 { 1359 schedulerLock.lock(); 1360 1361 try 1362 { 1363 return tasks.size(); 1364 } 1365 finally 1366 { 1367 schedulerLock.unlock(); 1368 } 1369 } 1370 1371 1372 1373 /** 1374 * Retrieves the number of recurring tasks in the task backend. 1375 * 1376 * @return The total number of entries in the task backend. 1377 */ 1378 public long getRecurringTaskCount() 1379 { 1380 schedulerLock.lock(); 1381 1382 try 1383 { 1384 return recurringTasks.size(); 1385 } 1386 finally 1387 { 1388 schedulerLock.unlock(); 1389 } 1390 } 1391 1392 1393 1394 /** 1395 * Retrieves the task backend with which this scheduler is associated. 1396 * 1397 * @return The task backend with which this scheduler is associated. 1398 */ 1399 public TaskBackend getTaskBackend() 1400 { 1401 return taskBackend; 1402 } 1403 1404 1405 1406 /** 1407 * Retrieves the root entry that is the common ancestor for all entries in the 1408 * task backend. 1409 * 1410 * @return The root entry that is the common ancestor for all entries in the 1411 * task backend. 1412 */ 1413 public Entry getTaskRootEntry() 1414 { 1415 return taskRootEntry.duplicate(true); 1416 } 1417 1418 1419 1420 /** 1421 * Retrieves the entry that is the immediate parent for all scheduled task 1422 * entries in the task backend. 1423 * 1424 * @return The entry that is the immediate parent for all scheduled task 1425 * entries in the task backend. 1426 */ 1427 public Entry getScheduledTaskParentEntry() 1428 { 1429 return scheduledTaskParentEntry.duplicate(true); 1430 } 1431 1432 1433 1434 /** 1435 * Retrieves the entry that is the immediate parent for all recurring task 1436 * entries in the task backend. 1437 * 1438 * @return The entry that is the immediate parent for all recurring task 1439 * entries in the task backend. 1440 */ 1441 public Entry getRecurringTaskParentEntry() 1442 { 1443 return recurringTaskParentEntry.duplicate(true); 1444 } 1445 1446 1447 1448 /** 1449 * Retrieves the scheduled task with the given task ID. 1450 * 1451 * @param taskID The task ID for the scheduled task to retrieve. 1452 * 1453 * @return The requested scheduled task, or <CODE>null</CODE> if there is no 1454 * such task. 1455 */ 1456 public Task getScheduledTask(String taskID) 1457 { 1458 schedulerLock.lock(); 1459 1460 try 1461 { 1462 return tasks.get(taskID); 1463 } 1464 finally 1465 { 1466 schedulerLock.unlock(); 1467 } 1468 } 1469 1470 1471 1472 /** 1473 * Retrieves the scheduled task created from the specified entry. 1474 * 1475 * @param taskEntryDN The DN of the task configuration entry associated 1476 * with the task to retrieve. 1477 * 1478 * @return The requested scheduled task, or <CODE>null</CODE> if there is no 1479 * such task. 1480 */ 1481 public Task getScheduledTask(DN taskEntryDN) 1482 { 1483 schedulerLock.lock(); 1484 1485 try 1486 { 1487 for (Task t : tasks.values()) 1488 { 1489 if (taskEntryDN.equals(t.getTaskEntry().getName())) 1490 { 1491 return t; 1492 } 1493 } 1494 1495 return null; 1496 } 1497 finally 1498 { 1499 schedulerLock.unlock(); 1500 } 1501 } 1502 1503 1504 1505 /** 1506 * Indicates whether the current thread already holds a lock on the scheduler. 1507 * 1508 * @return {@code true} if the current thread holds the scheduler lock, or 1509 * {@code false} if not. 1510 */ 1511 boolean holdsSchedulerLock() 1512 { 1513 return schedulerLock.isHeldByCurrentThread(); 1514 } 1515 1516 1517 1518 /** 1519 * Attempts to acquire a write lock on the specified entry, trying as many 1520 * times as necessary until the lock has been acquired. 1521 * 1522 * @param entryDN The DN of the entry for which to acquire the write lock. 1523 * 1524 * @return The write lock that has been acquired for the entry. 1525 */ 1526 DNLock writeLockEntry(DN entryDN) 1527 { 1528 DNLock lock = null; 1529 while (lock == null) 1530 { 1531 lock = DirectoryServer.getLockManager().tryWriteLockEntry(entryDN); 1532 } 1533 return lock; 1534 } 1535 1536 1537 1538 /** 1539 * Attempts to acquire a read lock on the specified entry. 1540 * 1541 * @param entryDN The DN of the entry for which to acquire the read lock. 1542 * 1543 * @return The read lock that has been acquired for the entry. 1544 * 1545 * @throws DirectoryException If the read lock cannot be acquired. 1546 */ 1547 DNLock readLockEntry(DN entryDN) throws DirectoryException 1548 { 1549 final DNLock lock = DirectoryServer.getLockManager().tryReadLockEntry(entryDN); 1550 if (lock != null) 1551 { 1552 return lock; 1553 } 1554 throw new DirectoryException(ResultCode.BUSY, ERR_BACKEND_CANNOT_LOCK_ENTRY.get(entryDN)); 1555 } 1556 1557 1558 1559 /** 1560 * Retrieves the scheduled task entry with the provided DN. The caller should 1561 * hold a read lock on the target entry. 1562 * 1563 * @param scheduledTaskEntryDN The entry DN that indicates which scheduled 1564 * task entry to retrieve. 1565 * 1566 * @return The scheduled task entry with the provided DN, or 1567 * <CODE>null</CODE> if no scheduled task has the provided DN. 1568 */ 1569 public Entry getScheduledTaskEntry(DN scheduledTaskEntryDN) 1570 { 1571 schedulerLock.lock(); 1572 1573 try 1574 { 1575 for (Task task : tasks.values()) 1576 { 1577 Entry taskEntry = task.getTaskEntry(); 1578 1579 if (scheduledTaskEntryDN.equals(taskEntry.getName())) 1580 { 1581 return taskEntry.duplicate(true); 1582 } 1583 } 1584 1585 return null; 1586 } 1587 finally 1588 { 1589 schedulerLock.unlock(); 1590 } 1591 } 1592 1593 1594 1595 /** 1596 * Compares the filter in the provided search operation against each of the 1597 * task entries, returning any that match. Note that only the search filter 1598 * will be used -- the base and scope will be ignored, so the caller must 1599 * ensure that they are correct for scheduled tasks. 1600 * 1601 * @param searchOperation The search operation to use when performing the 1602 * search. 1603 * 1604 * @return <CODE>true</CODE> if processing should continue on the search 1605 * operation, or <CODE>false</CODE> if it should not for some reason 1606 * (e.g., a size or time limit was reached). 1607 * 1608 * @throws DirectoryException If a problem occurs while processing the 1609 * search operation against the scheduled tasks. 1610 */ 1611 public boolean searchScheduledTasks(SearchOperation searchOperation) 1612 throws DirectoryException 1613 { 1614 SearchFilter filter = searchOperation.getFilter(); 1615 1616 schedulerLock.lock(); 1617 1618 try 1619 { 1620 for (Task t : tasks.values()) 1621 { 1622 DN taskEntryDN = t.getTaskEntryDN(); 1623 DNLock lock = readLockEntry(taskEntryDN); 1624 try 1625 { 1626 Entry e = t.getTaskEntry().duplicate(true); 1627 if (filter.matchesEntry(e) && !searchOperation.returnEntry(e, null)) 1628 { 1629 return false; 1630 } 1631 } 1632 finally 1633 { 1634 lock.unlock(); 1635 } 1636 } 1637 1638 return true; 1639 } 1640 finally 1641 { 1642 schedulerLock.unlock(); 1643 } 1644 } 1645 1646 1647 1648 /** 1649 * Retrieves the recurring task with the given recurring task ID. 1650 * 1651 * @param recurringTaskID The recurring task ID for the recurring task to 1652 * retrieve. 1653 * 1654 * @return The requested recurring task, or <CODE>null</CODE> if there is no 1655 * such recurring task. 1656 */ 1657 public RecurringTask getRecurringTask(String recurringTaskID) 1658 { 1659 schedulerLock.lock(); 1660 1661 try 1662 { 1663 return recurringTasks.get(recurringTaskID); 1664 } 1665 finally 1666 { 1667 schedulerLock.unlock(); 1668 } 1669 } 1670 1671 1672 1673 /** 1674 * Retrieves the recurring task with the given recurring task ID. 1675 * 1676 * @param recurringTaskEntryDN The recurring task ID for the recurring task 1677 * to retrieve. 1678 * 1679 * @return The requested recurring task, or <CODE>null</CODE> if there is no 1680 * such recurring task. 1681 */ 1682 public RecurringTask getRecurringTask(DN recurringTaskEntryDN) 1683 { 1684 schedulerLock.lock(); 1685 1686 try 1687 { 1688 for (RecurringTask rt : recurringTasks.values()) 1689 { 1690 if (recurringTaskEntryDN.equals(rt.getRecurringTaskEntry().getName())) 1691 { 1692 return rt; 1693 } 1694 } 1695 1696 return null; 1697 } 1698 finally 1699 { 1700 schedulerLock.unlock(); 1701 } 1702 } 1703 1704 1705 1706 /** 1707 * Retrieves the recurring task entry with the provided DN. The caller should 1708 * hold a read lock on the target entry. 1709 * 1710 * @param recurringTaskEntryDN The entry DN that indicates which recurring 1711 * task entry to retrieve. 1712 * 1713 * @return The recurring task entry with the provided DN, or 1714 * <CODE>null</CODE> if no recurring task has the provided DN. 1715 */ 1716 public Entry getRecurringTaskEntry(DN recurringTaskEntryDN) 1717 { 1718 schedulerLock.lock(); 1719 1720 try 1721 { 1722 for (RecurringTask recurringTask : recurringTasks.values()) 1723 { 1724 Entry recurringTaskEntry = recurringTask.getRecurringTaskEntry(); 1725 1726 if (recurringTaskEntryDN.equals(recurringTaskEntry.getName())) 1727 { 1728 return recurringTaskEntry.duplicate(true); 1729 } 1730 } 1731 1732 return null; 1733 } 1734 finally 1735 { 1736 schedulerLock.unlock(); 1737 } 1738 } 1739 1740 1741 1742 /** 1743 * Compares the filter in the provided search operation against each of the 1744 * recurring task entries, returning any that match. Note that only the 1745 * search filter will be used -- the base and scope will be ignored, so the 1746 * caller must ensure that they are correct for recurring tasks. 1747 * 1748 * @param searchOperation The search operation to use when performing the 1749 * search. 1750 * 1751 * @return <CODE>true</CODE> if processing should continue on the search 1752 * operation, or <CODE>false</CODE> if it should not for some reason 1753 * (e.g., a size or time limit was reached). 1754 * 1755 * @throws DirectoryException If a problem occurs while processing the 1756 * search operation against the recurring tasks. 1757 */ 1758 public boolean searchRecurringTasks(SearchOperation searchOperation) 1759 throws DirectoryException 1760 { 1761 SearchFilter filter = searchOperation.getFilter(); 1762 1763 schedulerLock.lock(); 1764 1765 try 1766 { 1767 for (RecurringTask rt : recurringTasks.values()) 1768 { 1769 DN recurringTaskEntryDN = rt.getRecurringTaskEntryDN(); 1770 DNLock lock = readLockEntry(recurringTaskEntryDN); 1771 try 1772 { 1773 Entry e = rt.getRecurringTaskEntry().duplicate(true); 1774 if (filter.matchesEntry(e) && ! searchOperation.returnEntry(e, null)) 1775 { 1776 return false; 1777 } 1778 } 1779 finally 1780 { 1781 lock.unlock(); 1782 } 1783 } 1784 1785 return true; 1786 } 1787 finally 1788 { 1789 schedulerLock.unlock(); 1790 } 1791 } 1792 1793 1794 1795 /** 1796 * Decodes the contents of the provided entry as a scheduled task. The 1797 * resulting task will not actually be scheduled for processing. 1798 * 1799 * @param entry The entry to decode as a scheduled task. 1800 * @param operation The operation used to create this task in the server, or 1801 * {@code null} if the operation is not available. 1802 * 1803 * @return The scheduled task decoded from the provided entry. 1804 * 1805 * @throws DirectoryException If the provided entry cannot be decoded as a 1806 * scheduled task. 1807 */ 1808 public Task entryToScheduledTask(Entry entry, Operation operation) 1809 throws DirectoryException 1810 { 1811 // Get the name of the class that implements the task logic. 1812 AttributeType attrType = DirectoryServer.getAttributeType(ATTR_TASK_CLASS); 1813 List<Attribute> attrList = entry.getAttribute(attrType); 1814 if (attrList.isEmpty()) 1815 { 1816 LocalizableMessage message = ERR_TASKSCHED_NO_CLASS_ATTRIBUTE.get(ATTR_TASK_ID); 1817 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 1818 } 1819 1820 if (attrList.size() > 1) 1821 { 1822 LocalizableMessage message = ERR_TASKSCHED_MULTIPLE_CLASS_TYPES.get(ATTR_TASK_ID); 1823 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 1824 } 1825 1826 Attribute attr = attrList.get(0); 1827 if (attr.isEmpty()) 1828 { 1829 LocalizableMessage message = ERR_TASKSCHED_NO_CLASS_VALUES.get(ATTR_TASK_ID); 1830 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 1831 } 1832 1833 Iterator<ByteString> iterator = attr.iterator(); 1834 ByteString value = iterator.next(); 1835 if (iterator.hasNext()) 1836 { 1837 LocalizableMessage message = ERR_TASKSCHED_MULTIPLE_CLASS_VALUES.get(ATTR_TASK_ID); 1838 throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message); 1839 } 1840 1841 // Try to load the specified class. 1842 String taskClassName = value.toString(); 1843 Class<?> taskClass; 1844 try 1845 { 1846 taskClass = DirectoryServer.loadClass(taskClassName); 1847 } 1848 catch (Exception e) 1849 { 1850 logger.traceException(e); 1851 1852 LocalizableMessage message = ERR_TASKSCHED_CANNOT_LOAD_CLASS. 1853 get(taskClassName, ATTR_TASK_CLASS, stackTraceToSingleLineString(e)); 1854 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 1855 } 1856 1857 // Instantiate the class as a task. 1858 Task task; 1859 try 1860 { 1861 task = (Task) taskClass.newInstance(); 1862 } 1863 catch (Exception e) 1864 { 1865 logger.traceException(e); 1866 1867 LocalizableMessage message = ERR_TASKSCHED_CANNOT_INSTANTIATE_CLASS_AS_TASK.get( 1868 taskClassName, Task.class.getName()); 1869 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 1870 } 1871 1872 // Perform the necessary internal and external initialization for the task. 1873 try 1874 { 1875 task.initializeTaskInternal(serverContext, this, entry); 1876 } 1877 catch (InitializationException ie) 1878 { 1879 logger.traceException(ie); 1880 1881 LocalizableMessage message = ERR_TASKSCHED_CANNOT_INITIALIZE_INTERNAL.get( 1882 taskClassName, ie.getMessage()); 1883 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 1884 } 1885 catch (Exception e) 1886 { 1887 LocalizableMessage message = ERR_TASKSCHED_CANNOT_INITIALIZE_INTERNAL.get( 1888 taskClassName, stackTraceToSingleLineString(e)); 1889 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); 1890 } 1891 1892 if (!TaskState.isDone(task.getTaskState()) && 1893 !DirectoryServer.getAllowedTasks().contains(taskClassName)) 1894 { 1895 LocalizableMessage message = ERR_TASKSCHED_NOT_ALLOWED_TASK.get(taskClassName); 1896 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 1897 } 1898 1899 task.setOperation(operation); 1900 1901 // Avoid task specific initialization for completed tasks. 1902 if (!TaskState.isDone(task.getTaskState())) { 1903 task.initializeTask(); 1904 } 1905 task.setOperation(null); 1906 1907 return task; 1908 } 1909 1910 1911 1912 /** 1913 * Decodes the contents of the provided entry as a recurring task. The 1914 * resulting recurring task will not actually be added to the scheduler. 1915 * 1916 * @param entry The entry to decode as a recurring task. 1917 * 1918 * @return The recurring task decoded from the provided entry. 1919 * 1920 * @throws DirectoryException If the provided entry cannot be decoded as a 1921 * recurring task. 1922 */ 1923 public RecurringTask entryToRecurringTask(Entry entry) 1924 throws DirectoryException 1925 { 1926 return new RecurringTask(serverContext, this, entry); 1927 } 1928 1929 1930 1931 /** 1932 * Retrieves the DN of the configuration entry with which this alert generator 1933 * is associated. 1934 * 1935 * @return The DN of the configuration entry with which this alert generator 1936 * is associated. 1937 */ 1938 @Override 1939 public DN getComponentEntryDN() 1940 { 1941 return taskBackend.getConfigEntryDN(); 1942 } 1943 1944 1945 1946 /** 1947 * Retrieves the fully-qualified name of the Java class for this alert 1948 * generator implementation. 1949 * 1950 * @return The fully-qualified name of the Java class for this alert 1951 * generator implementation. 1952 */ 1953 @Override 1954 public String getClassName() 1955 { 1956 return CLASS_NAME; 1957 } 1958 1959 1960 1961 /** 1962 * Retrieves information about the set of alerts that this generator may 1963 * produce. The map returned should be between the notification type for a 1964 * particular notification and the human-readable description for that 1965 * notification. This alert generator must not generate any alerts with types 1966 * that are not contained in this list. 1967 * 1968 * @return Information about the set of alerts that this generator may 1969 * produce. 1970 */ 1971 @Override 1972 public LinkedHashMap<String,String> getAlerts() 1973 { 1974 LinkedHashMap<String, String> alerts = new LinkedHashMap<>(); 1975 1976 alerts.put(ALERT_TYPE_CANNOT_SCHEDULE_RECURRING_ITERATION, 1977 ALERT_DESCRIPTION_CANNOT_SCHEDULE_RECURRING_ITERATION); 1978 alerts.put(ALERT_TYPE_CANNOT_RENAME_CURRENT_TASK_FILE, 1979 ALERT_DESCRIPTION_CANNOT_RENAME_CURRENT_TASK_FILE); 1980 alerts.put(ALERT_TYPE_CANNOT_RENAME_NEW_TASK_FILE, 1981 ALERT_DESCRIPTION_CANNOT_RENAME_NEW_TASK_FILE); 1982 alerts.put(ALERT_TYPE_CANNOT_WRITE_TASK_FILE, 1983 ALERT_DESCRIPTION_CANNOT_WRITE_TASK_FILE); 1984 1985 return alerts; 1986 } 1987}