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