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