Task.java revision 998747bfaaa3c6b28bbfaf0e282e6c0ccbf46bc0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2009 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
/**
* This class defines a task that may be executed by the task backend within the
* Directory Server.
*/
public abstract class Task
implements Comparable<Task>
{
// The DN for the task entry.
private DN taskEntryDN;
// The entry that actually defines this task.
// The action to take if one of the dependencies for this task does not
// complete successfully.
// The counter used for log messages associated with this task.
private int logMessageCounter;
// The task IDs of other tasks on which this task is dependent.
// A set of log messages generated by this task.
// TODO: convert from String to LocalizableMessage objects.
// Since these are stored in an entry we would need
// to adopt some way for writing message to string in such
// a way that the information could be reparsed from its
// string value.
// The set of e-mail addresses of the users to notify when the task is done
// running, regardless of whether it completes successfully.
// The set of e-mail addresses of the users to notify if the task does not
// complete successfully for some reason.
// 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 operation used to create this task in the server.
// The ID of the recurring task with which this task is associated.
private String recurringTaskID;
// The unique ID assigned to this task.
// The task backend with which this task is associated.
private TaskBackend taskBackend;
// The current state of this task.
// 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;
/**
* Gets a message that identifies this type of task suitable for
* presentation to humans in monitoring tools.
*
* @return name of task
*/
public LocalizableMessage getDisplayName() {
// NOTE: this method is invoked via reflection. If you rename
// it be sure to modify the calls.
return null;
};
/**
* Given an attribute type name returns and locale sensitive
* representation.
*
* @param name of an attribute type associated with the object
* class that represents this entry in the directory
* @return LocalizableMessage diaplay name
*/
// Subclasses that are schedulable from the task interface
// should override this
// NOTE: this method is invoked via reflection. If you rename
// it be sure to modify the calls.
return null;
}
/**
* Performs generic initialization for this task based on the information in
* the provided task entry.
*
* @param taskScheduler The scheduler with which this task is associated.
* @param taskEntry The entry containing the task configuration.
*
* @throws InitializationException If a problem occurs while performing the
* initialization.
*/
throws InitializationException
{
this.taskScheduler = taskScheduler;
// 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.
{
if (recurringTaskID == null)
{
}
else
{
}
}
// Get the current state from the task. If there is none, then assume it's
// a new task.
if (stateString == null)
{
}
else
{
{
throw new InitializationException(message);
}
}
// 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;
false);
if (timeString != null)
{
{
}
else
{
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
throw new InitializationException(message, e);
}
}
// Get the actual start time for the task, if there is one.
actualStartTime = -1;
if (timeString != null)
{
{
}
else
{
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
throw new InitializationException(message, e);
}
}
// Get the completion time for the task, if there is one.
completionTime = -1;
if (timeString != null)
{
{
}
else
{
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
throw new InitializationException(message, e);
}
}
// Get information about any dependencies that the task might have.
false);
if (actionString != null)
{
if (failedDependencyAction == null)
{
}
}
// Get the information about the e-mail addresses to use for notification
// purposes.
// Get the log messages for the task.
if (logMessages != null) {
}
}
/**
* Retrieves the single value for the requested attribute as a string.
*
* @param attributeName The name of the attribute for which to retrieve the
* value.
* @param isRequired Indicates whether the attribute is required to have
* a value.
*
* @return The value for the requested attribute, or <CODE>null</CODE> if it
* is not present in the entry and is not required.
*
* @throws InitializationException If the requested attribute is not present
* in the entry but is required, or if there
* are multiple instances of the requested
* attribute in the entry with different
* sets of options, or if there are multiple
* values for the requested attribute.
*/
throws InitializationException
{
{
if (isRequired)
{
}
else
{
return null;
}
}
{
throw new InitializationException(ERR_TASK_MULTIPLE_ATTRS_FOR_TYPE.get(attributeName, taskEntry.getName()));
}
{
if (isRequired)
{
throw new InitializationException(ERR_TASK_NO_VALUES_FOR_ATTR.get(attributeName, taskEntry.getName()));
}
else
{
return null;
}
}
{
throw new InitializationException(ERR_TASK_MULTIPLE_VALUES_FOR_ATTR.get(attributeName, taskEntry.getName()));
}
}
/**
* Retrieves the values for the requested attribute as a list of strings.
*
* @param attributeName The name of the attribute for which to retrieve the
* values.
*
* @return The list of values for the requested attribute, or an empty list
* if the attribute does not exist or does not have any values.
*
* @throws InitializationException If there are multiple instances of the
* requested attribute in the entry with
* different sets of options.
*/
throws InitializationException
{
{
return valueStrings;
}
{
throw new InitializationException(ERR_TASK_MULTIPLE_ATTRS_FOR_TYPE.get(attributeName, taskEntry.getName()));
}
{
}
return valueStrings;
}
/**
* Retrieves the DN of the entry containing the definition for this task.
*
* @return The DN of the entry containing the definition for this task.
*/
public final DN getTaskEntryDN()
{
return taskEntryDN;
}
/**
* Retrieves the entry containing the definition for this task.
*
* @return The entry containing the definition for this task.
*/
public final Entry getTaskEntry()
{
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,
* and it should only be accessed from within the {@code initializeTask}
* method (and even that method should not depend on it always being
* available, since it will not be available if the server is restarted and
* the task needs to be reinitialized).
*
* @return The operation used to create this task in the server, or
* {@code null} if it is not available.
*/
public final Operation getOperation()
{
return operation;
}
/**
* Specifies the operation used to create this task in the server.
*
* @param operation The operation used to create this task in the server.
*/
{
}
/**
* Retrieves the unique identifier assigned to this task.
*
* @return 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 The unique identifier assigned to the recurring task that is
* associated with this task, or <CODE>null</CODE> if it is not
* associated with any recurring task.
*/
public final String getRecurringTaskID()
{
return recurringTaskID;
}
/**
* Retrieves the current state for this task.
*
* @return The current state for this task.
*/
public final TaskState getTaskState()
{
return taskState;
}
/**
* Indicates whether or not this task is an iteration of
* some recurring task.
*
* @return boolean where true indicates that this task is
* recurring, false otherwise.
*/
public boolean isRecurring()
{
return (recurringTaskID != null);
}
/**
* Indicates whether or not this task has been cancelled.
*
* @return boolean where true indicates that this task was
* cancelled either before or during execution
*/
public boolean isCancelled()
{
return taskInterruptState != null &&
}
/**
* Sets the state for this task and updates the associated task entry as
* necessary. It does not automatically persist the updated task information
* to disk.
*
* @param taskState The new state to use for the task.
*/
{
// We only need to grab the entry-level lock if we don't already hold the
// broader scheduler lock.
if (needLock)
{
}
try
{
}
finally
{
if (needLock)
{
}
}
}
/**
* Sets a state for this task that is the result of a call to
* {@link #interruptTask(TaskState, org.opends.messages.LocalizableMessage)}.
* It may take this task some time to actually cancel to that
* actual state may differ until quiescence.
*
* @param state for this task once it has canceled whatever it is doing
*/
{
this.taskInterruptState = state;
}
/**
* Gets the interrupt state for this task that was set as a
* result of a call to {@link #interruptTask(TaskState,
* org.opends.messages.LocalizableMessage)}.
*
* @return interrupt state for this task
*/
protected TaskState getTaskInterruptState()
{
return this.taskInterruptState;
}
/**
* Returns a state for this task after processing has completed.
* If the task was interrupted with a call to
* {@link #interruptTask(TaskState, org.opends.messages.LocalizableMessage)}
* then that method's interruptState is returned here. Otherwse
* this method returns TaskState.COMPLETED_SUCCESSFULLY. It is
* assumed that if there were errors during task processing that
* task state will have been derived in some other way.
*
* @return state for this task after processing has completed
*/
protected TaskState getFinalTaskState()
{
if (this.taskInterruptState == null)
{
return TaskState.COMPLETED_SUCCESSFULLY;
}
else
{
return this.taskInterruptState;
}
}
/**
* Replaces an attribute values of the task entry.
*
* @param name The name of the attribute that must be replaced.
*
* @param value The value that must replace the previous values of the
* attribute.
*
* @throws DirectoryException When an error occurs.
*/
throws DirectoryException
{
// We only need to grab the entry-level lock if we don't already hold the
// broader scheduler lock.
if (needLock)
{
}
try
{
}
finally
{
if (needLock)
{
}
}
}
/**
* 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
* <CODE>System.currentTimeMillis()</CODE>. Any value representing a time in
* the past, or any negative value, should be taken to mean that the task
* should be considered eligible for immediate execution.
*
* @return The scheduled start time for this task.
*/
public final long getScheduledStartTime()
{
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
* for <CODE>System.currentTimeMillis()</CODE>.
*
* @return The time that this task actually started running, or -1 if it has
* not yet been started.
*/
public final long getActualStartTime()
{
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
* information to disk.
*
* @param actualStartTime The actual start time to use for this task.
*/
private void setActualStartTime(long actualStartTime)
{
// We only need to grab the entry-level lock if we don't already hold the
// broader scheduler lock.
if (needLock)
{
}
try
{
this.actualStartTime = actualStartTime;
}
finally
{
if (needLock)
{
}
}
}
/**
* Retrieves the time that this task completed all of its associated
* processing (regardless of whether it was successful), if it has completed.
* The value returned will be in the same format as the return value for
* <CODE>System.currentTimeMillis()</CODE>.
*
* @return The time that this task actually completed running, or -1 if it
* has not yet completed.
*/
public final long getCompletionTime()
{
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
* information to disk.
*
* @param completionTime The completion time to use for this task.
*/
protected void setCompletionTime(long completionTime)
{
// We only need to grab the entry-level lock if we don't already hold the
// broader scheduler lock.
if (needLock)
{
}
try
{
this.completionTime = completionTime;
dateFormat.format(d));
}
finally
{
if (needLock)
{
}
}
}
/**
* 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 The set of task IDs for any tasks on which this task is dependent.
*/
{
return dependencyIDs;
}
/**
* Retrieves the action that should be taken if any of the dependencies for
* this task do not complete successfully.
*
* @return The action that should be taken if any of the dependencies for
* this task do not complete successfully.
*/
public final FailedDependencyAction getFailedDependencyAction()
{
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
* notification will be sent to these users regardless of whether the task
* completed successfully. This list must not be directly modified by the
* caller.
*
* @return The set of e-mail addresses for the users that should receive a
* notification message when processing for this task has
* completed.
*/
{
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
* successfully. This list must not be directly modified by the caller.
*
* @return The set of e-mail addresses for the users that should receive a
* notification message if processing for this task does not complete
* successfully.
*/
{
return notifyOnError;
}
/**
* Retrieves the set of messages that were logged by this task. This list
* must not be directly modified by the caller.
*
* @return The set of messages that were logged by this task.
*/
{
// TODO: a better job or recreating the message
}
}
/**
* Adds a log message to the set of messages logged by this task. This method
* should not be called directly by tasks, but rather will be called
* indirectly through the {@code ErrorLog.logError} methods. It does not
* automatically persist the updated task information to disk.
*
* @param severity
* the severity of message.
* @param message
* the log message.
*/
}
/**
* Adds a log message to the set of messages logged by this task. This method
* should not be called directly by tasks, but rather will be called
* indirectly through the {@code ErrorLog.logError} methods. It does not
* automatically persist the updated task information to disk.
*
* @param severity
* the severity of message.
* @param message
* the log message.
* @param exception
* the exception to log. May be {@code null}.
*/
{
// We cannot do task logging if the schema is either destroyed or
// not initialized eg during in-core restart from Restart task.
// Bailing out if there is no schema available saves us from NPE.
{
return;
}
// We only need to grab the entry-level lock if we don't already hold the
// broader scheduler lock.
if (needLock)
{
}
try
{
{
}
{
}
{
}
{
}
else
{
}
}
finally
{
if (needLock)
{
}
}
}
/**
* 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
* uncompleted task. If both tasks are completed, then they will be ordered
* by completion time. If both tasks are uncompleted, then a running task
* will always be ordered before one that has not started. If both are
* running, then they will be ordered by actual start time. If neither have
* started, then they will be ordered by scheduled start time. If all else
* fails, they will be ordered lexicographically by task ID.
*
* @param task The task to compare with this task.
*
* @return A negative value if the provided task should come before this
* task, a positive value if the provided task should come after this
* task, or zero if there is no difference with regard to their
* order.
*/
{
if (completionTime > 0)
{
{
// They have both completed, so order by completion time.
{
return -1;
}
{
return 1;
}
else
{
// They have the same completion time, so order by task ID.
}
}
else
{
// Completed tasks are always ordered before those that haven't
// completed.
return -1;
}
}
{
// Completed tasks are always ordered before those that haven't completed.
return 1;
}
if (actualStartTime > 0)
{
{
// They are both running, so order by actual start time.
{
return -1;
}
{
return 1;
}
else
{
// They have the same actual start time, so order by task ID.
}
}
else
{
// Running tasks are always ordered before those that haven't started.
return -1;
}
}
{
// Running tasks are always ordered before those that haven't started.
return 1;
}
// Neither task has started, so order by scheduled start time, or if nothing
// else by task ID.
{
return -1;
}
{
return 1;
}
else
{
}
}
/**
* Begins execution for this task. This is a wrapper around the
* <CODE>runTask</CODE> method that performs the appropriate set-up and
* tear-down. It should only be invoked by a task thread.
*
* @return The final state to use for the task.
*/
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
return taskState;
}
/**
* If appropriate, send an e-mail message with information about the
* completed task.
*
* @throws MessagingException If a problem occurs while attempting to send
* the message.
*/
protected void sendNotificationEMailMessage()
throws MessagingException
{
{
{
}
if (! recipients.isEmpty())
{
if (scheduledStartTime <= 0)
{
scheduledStartDate = "";
}
else
{
}
{
}
}
}
}
/**
* Performs any task-specific initialization that may be required before
* processing can start. This default implementation does not do anything,
* but subclasses may override it as necessary. This method will be called at
* the time the task is scheduled, and therefore any failure in this method
* will be returned to the client.
*
* @throws DirectoryException If a problem occurs during initialization that
* should be returned to the client.
*/
public void initializeTask()
throws DirectoryException
{
// 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.
*
* @return The final state to use for the task.
*/
/**
* 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
* {@link #setTaskInterruptState(TaskState)} if the interruption is accepted
* by this task.
*
* @param interruptState The state to use for the task if it is
* successfully interrupted.
* @param interruptReason A human-readable explanation for the cancellation.
*/
{
// No action is performed by default.
// NOTE: if you implement this make sure to override isInterruptable
// to return 'true'
}
/**
* Indicates whether or not this task is interruptable or not.
*
* @return boolean where true indicates that this task can be interrupted.
*/
public boolean isInterruptable() {
return false;
}
}