| | |
| | | import javax.mail.MessagingException; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | |
| | | * This class defines a task that may be executed by the task backend within the |
| | | * Directory Server. |
| | | */ |
| | | public abstract class Task |
| | | implements Comparable<Task> |
| | | public abstract class Task implements Comparable<Task> |
| | | { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | |
| | | |
| | | /** The DN for the task entry. */ |
| | | private DN taskEntryDN; |
| | | |
| | | /** The entry that actually defines this task. */ |
| | | private Entry taskEntry; |
| | | |
| | | /** |
| | | * The action to take if one of the dependencies for this task does not |
| | | * complete successfully. |
| | | */ |
| | | /** The action to take if one of the dependencies for this task does not complete successfully. */ |
| | | private FailedDependencyAction failedDependencyAction; |
| | | |
| | | /** The counter used for log messages associated with this task. */ |
| | |
| | | * a way that the information could be reparsed from its |
| | | * string value. |
| | | */ |
| | | private LinkedList<String> logMessages; |
| | | private List<String> logMessages; |
| | | |
| | | /** |
| | | * The set of e-mail addresses of the users to notify when the task is done |
| | |
| | | |
| | | /** The time that processing actually started for this task. */ |
| | | private long actualStartTime; |
| | | |
| | | /** The time that actual processing ended for this task. */ |
| | | private long completionTime; |
| | | |
| | | /** The time that this task was scheduled to start processing. */ |
| | | private long scheduledStartTime; |
| | | |
| | |
| | | |
| | | /** The unique ID assigned to this task. */ |
| | | private String taskID; |
| | | |
| | | /** The task backend with which this task is associated. */ |
| | | private TaskBackend taskBackend; |
| | | |
| | | /** The current state of this task. */ |
| | | private TaskState taskState; |
| | | |
| | | /** The task state that may be set when the task is interrupted. */ |
| | | private TaskState taskInterruptState; |
| | | |
| | | /** The scheduler with which this task is associated. */ |
| | | private TaskScheduler taskScheduler; |
| | | |
| | |
| | | |
| | | taskBackend = taskScheduler.getTaskBackend(); |
| | | |
| | | |
| | | // Get the task ID and recurring task ID values. At least one of them must |
| | | // be provided. If it's a recurring task and there is no task ID, then |
| | | // generate one on the fly. |
| | |
| | | { |
| | | throw new InitializationException(ERR_TASK_MISSING_ATTR.get(taskEntry.getName(), ATTR_TASK_ID)); |
| | | } |
| | | else |
| | | { |
| | | taskID = UUID.randomUUID().toString(); |
| | | } |
| | | taskID = UUID.randomUUID().toString(); |
| | | } |
| | | |
| | | |
| | | // Get the current state from the task. If there is none, then assume it's |
| | | // a new task. |
| | | String stateString = getAttributeValue(ATTR_TASK_STATE, false); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // Get the scheduled start time for the task, if there is one. It may be |
| | | // in either UTC time (a date followed by a 'Z') or in the local time zone |
| | | // (not followed by a 'Z'). |
| | | scheduledStartTime = -1; |
| | | String timeString = getAttributeValue(ATTR_TASK_SCHEDULED_START_TIME, |
| | | false); |
| | | if (timeString != null) |
| | | { |
| | | SimpleDateFormat dateFormat; |
| | | if (timeString.endsWith("Z")) |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| | | } |
| | | else |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME); |
| | | } |
| | | |
| | | try |
| | | { |
| | | scheduledStartTime = dateFormat.parse(timeString).getTime(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = |
| | | ERR_TASK_CANNOT_PARSE_SCHEDULED_START_TIME.get(timeString, taskDN); |
| | | throw new InitializationException(message, e); |
| | | } |
| | | } |
| | | |
| | | scheduledStartTime = getTime(taskDN, ATTR_TASK_SCHEDULED_START_TIME, ERR_TASK_CANNOT_PARSE_SCHEDULED_START_TIME); |
| | | |
| | | // Get the actual start time for the task, if there is one. |
| | | actualStartTime = -1; |
| | | timeString = getAttributeValue(ATTR_TASK_ACTUAL_START_TIME, false); |
| | | if (timeString != null) |
| | | { |
| | | SimpleDateFormat dateFormat; |
| | | if (timeString.endsWith("Z")) |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| | | } |
| | | else |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME); |
| | | } |
| | | |
| | | try |
| | | { |
| | | actualStartTime = dateFormat.parse(timeString).getTime(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = |
| | | ERR_TASK_CANNOT_PARSE_ACTUAL_START_TIME.get(timeString, taskDN); |
| | | throw new InitializationException(message, e); |
| | | } |
| | | } |
| | | |
| | | actualStartTime = getTime(taskDN, ATTR_TASK_ACTUAL_START_TIME, ERR_TASK_CANNOT_PARSE_ACTUAL_START_TIME); |
| | | |
| | | // Get the completion time for the task, if there is one. |
| | | completionTime = -1; |
| | | timeString = getAttributeValue(ATTR_TASK_COMPLETION_TIME, false); |
| | | if (timeString != null) |
| | | { |
| | | SimpleDateFormat dateFormat; |
| | | if (timeString.endsWith("Z")) |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| | | } |
| | | else |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME); |
| | | } |
| | | |
| | | try |
| | | { |
| | | completionTime = dateFormat.parse(timeString).getTime(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = |
| | | ERR_TASK_CANNOT_PARSE_COMPLETION_TIME.get(timeString, taskDN); |
| | | throw new InitializationException(message, e); |
| | | } |
| | | } |
| | | |
| | | completionTime = getTime(taskDN, ATTR_TASK_COMPLETION_TIME, ERR_TASK_CANNOT_PARSE_COMPLETION_TIME); |
| | | |
| | | // Get information about any dependencies that the task might have. |
| | | dependencyIDs = getAttributeValues(ATTR_TASK_DEPENDENCY_IDS); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // Get the information about the e-mail addresses to use for notification |
| | | // purposes. |
| | | // Get the information about the e-mail addresses to use for notification purposes |
| | | notifyOnCompletion = getAttributeValues(ATTR_TASK_NOTIFY_ON_COMPLETION); |
| | | notifyOnError = getAttributeValues(ATTR_TASK_NOTIFY_ON_ERROR); |
| | | |
| | | |
| | | // Get the log messages for the task. |
| | | logMessages = getAttributeValues(ATTR_TASK_LOG_MESSAGES); |
| | | if (logMessages != null) { |
| | |
| | | } |
| | | } |
| | | |
| | | private long getTime(String taskDN, String attrName, Arg2<Object, Object> errorMsg) throws InitializationException |
| | | { |
| | | String timeString = getAttributeValue(attrName, false); |
| | | if (timeString != null) |
| | | { |
| | | SimpleDateFormat dateFormat; |
| | | if (timeString.endsWith("Z")) |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); |
| | | dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); |
| | | } |
| | | else |
| | | { |
| | | dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME); |
| | | } |
| | | |
| | | try |
| | | { |
| | | return dateFormat.parse(timeString).getTime(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | throw new InitializationException(errorMsg.get(timeString, taskDN), e); |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the single value for the requested attribute as a string. |
| | |
| | | return value.toString(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the values for the requested attribute as a list of strings. |
| | | * |
| | |
| | | * requested attribute in the entry with |
| | | * different sets of options. |
| | | */ |
| | | private LinkedList<String> getAttributeValues(String attributeName) |
| | | throws InitializationException |
| | | private LinkedList<String> getAttributeValues(String attributeName) throws InitializationException |
| | | { |
| | | LinkedList<String> valueStrings = new LinkedList<>(); |
| | | |
| | | List<Attribute> attrList = taskEntry.getAttribute(attributeName.toLowerCase()); |
| | | if (attrList == null || attrList.isEmpty()) |
| | | { |
| | |
| | | return valueStrings; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry containing the definition for this task. |
| | | * |
| | |
| | | return taskEntryDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the entry containing the definition for this task. |
| | | * |
| | |
| | | return taskEntry; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the operation used to create this task in the server. Note that |
| | | * this will only be available when the task is first added to the scheduler, |
| | |
| | | return operation; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Specifies the operation used to create this task in the server. |
| | | * |
| | |
| | | this.operation = operation; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the unique identifier assigned to this task. |
| | | * |
| | |
| | | return taskID; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the unique identifier assigned to the recurring task that is |
| | | * associated with this task, if there is one. |
| | |
| | | return recurringTaskID; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the current state for this task. |
| | | * |
| | |
| | | TaskState.isCancelled(taskInterruptState); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sets the state for this task and updates the associated task entry as |
| | | * necessary. It does not automatically persist the updated task information |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Sets a state for this task that is the result of a call to |
| | | * {@link #interruptTask(TaskState, LocalizableMessage)}. |
| | |
| | | this.taskInterruptState = state; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Gets the interrupt state for this task that was set as a |
| | | * result of a call to {@link #interruptTask(TaskState, LocalizableMessage)}. |
| | |
| | | return this.taskInterruptState; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns a state for this task after processing has completed. |
| | | * If the task was interrupted with a call to |
| | |
| | | */ |
| | | protected TaskState getFinalTaskState() |
| | | { |
| | | if (this.taskInterruptState == null) |
| | | { |
| | | return TaskState.COMPLETED_SUCCESSFULLY; |
| | | } |
| | | else |
| | | if (this.taskInterruptState != null) |
| | | { |
| | | return this.taskInterruptState; |
| | | } |
| | | return TaskState.COMPLETED_SUCCESSFULLY; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces an attribute values of the task entry. |
| | | * |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Retrieves the scheduled start time for this task, if there is one. The |
| | | * value returned will be in the same format as the return value for |
| | |
| | | return scheduledStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time that this task actually started running, if it has |
| | | * started. The value returned will be in the same format as the return value |
| | |
| | | return actualStartTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sets the actual start time for this task and updates the associated task |
| | | * entry as necessary. It does not automatically persist the updated task |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the time that this task completed all of its associated |
| | | * processing (regardless of whether it was successful), if it has completed. |
| | |
| | | return completionTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sets the completion time for this task and updates the associated task |
| | | * entry as necessary. It does not automatically persist the updated task |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of task IDs for any tasks on which this task is |
| | | * dependent. This list must not be directly modified by the caller. |
| | |
| | | return dependencyIDs; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the action that should be taken if any of the dependencies for |
| | | * this task do not complete successfully. |
| | |
| | | return failedDependencyAction; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of e-mail addresses for the users that should receive a |
| | | * notification message when processing for this task has completed. This |
| | |
| | | return notifyOnCompletion; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of e-mail addresses for the users that should receive a |
| | | * notification message if processing for this task does not complete |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Compares this task with the provided task for the purposes of ordering in a |
| | | * sorted list. Any completed task will always be ordered before an |
| | |
| | | { |
| | | if (completionTime > 0) |
| | | { |
| | | if (task.completionTime > 0) |
| | | { |
| | | // They have both completed, so order by completion time. |
| | | if (completionTime < task.completionTime) |
| | | { |
| | | return -1; |
| | | } |
| | | else if (completionTime > task.completionTime) |
| | | { |
| | | return 1; |
| | | } |
| | | else |
| | | { |
| | | // They have the same completion time, so order by task ID. |
| | | return taskID.compareTo(task.taskID); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Completed tasks are always ordered before those that haven't |
| | | // completed. |
| | | return -1; |
| | | } |
| | | return compareTimes(task, completionTime, task.completionTime); |
| | | } |
| | | else if (task.completionTime > 0) |
| | | { |
| | |
| | | |
| | | if (actualStartTime > 0) |
| | | { |
| | | if (task.actualStartTime > 0) |
| | | { |
| | | // They are both running, so order by actual start time. |
| | | if (actualStartTime < task.actualStartTime) |
| | | { |
| | | return -1; |
| | | } |
| | | else if (actualStartTime > task.actualStartTime) |
| | | { |
| | | return 1; |
| | | } |
| | | else |
| | | { |
| | | // They have the same actual start time, so order by task ID. |
| | | return taskID.compareTo(task.taskID); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Running tasks are always ordered before those that haven't started. |
| | | return -1; |
| | | } |
| | | return compareTimes(task, actualStartTime, task.actualStartTime); |
| | | } |
| | | else if (task.actualStartTime > 0) |
| | | { |
| | |
| | | return 1; |
| | | } |
| | | |
| | | |
| | | // Neither task has started, so order by scheduled start time, or if nothing |
| | | // else by task ID. |
| | | if (scheduledStartTime < task.scheduledStartTime) |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | private int compareTimes(Task task, long time1, long time2) |
| | | { |
| | | if (time2 > 0) |
| | | { |
| | | // They are both running, so order by actual start time. |
| | | // OR they have both completed, so order by completion time. |
| | | if (time1 < time2) |
| | | { |
| | | return -1; |
| | | } |
| | | else if (time1 > time2) |
| | | { |
| | | return 1; |
| | | } |
| | | else |
| | | { |
| | | // They have the same actual start/completion time, so order by task ID. |
| | | return taskID.compareTo(task.taskID); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // Running tasks are always ordered before those that haven't started. |
| | | // OR completed tasks are always ordered before those that haven't completed. |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Begins execution for this task. This is a wrapper around the |
| | |
| | | setTaskState(TaskState.RUNNING); |
| | | taskScheduler.writeState(); |
| | | |
| | | TaskState taskState = this.taskState; |
| | | |
| | | try |
| | | { |
| | | taskState = runTask(); |
| | | return runTask(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | taskState = TaskState.STOPPED_BY_ERROR; |
| | | |
| | | logger.error(ERR_TASK_EXECUTE_FAILED, taskEntry.getName(), stackTraceToSingleLineString(e)); |
| | | return TaskState.STOPPED_BY_ERROR; |
| | | } |
| | | |
| | | return taskState; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * If appropriate, send an e-mail message with information about the |
| | | * completed task. |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs any task-specific initialization that may be required before |
| | | * processing can start. This default implementation does not do anything, |
| | |
| | | // No action is performed by default. |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs the actual core processing for this task. This method should not |
| | | * return until all processing associated with this task has completed. |
| | |
| | | */ |
| | | protected abstract TaskState runTask(); |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs any necessary processing to prematurely interrupt the execution of |
| | | * this task. By default no action is performed, but if it is feasible to |
| | | * gracefully interrupt a task, then subclasses should override this method to |
| | | * do so. |
| | | * |
| | | * Implementations of this method are exprected to call |
| | | * Implementations of this method are expected to call |
| | | * {@link #setTaskInterruptState(TaskState)} if the interruption is accepted |
| | | * by this task. |
| | | * |
| | |
| | | { |
| | | // No action is performed by default. |
| | | |
| | | // NOTE: if you implement this make sure to override isInterruptable |
| | | // to return 'true' |
| | | // NOTE: if you implement this make sure to override isInterruptable() to return 'true' |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether or not this task is interruptable or not. |
| | | * Indicates whether or not this task is interruptible or not. |
| | | * |
| | | * @return boolean where true indicates that this task can be interrupted. |
| | | */ |
| | | public boolean isInterruptable() { |
| | | return false; |
| | | } |
| | | |
| | | } |
| | | |