/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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. */ package org.opends.server.backends.task; import static org.opends.messages.BackendMessages.*; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.locks.Lock; import javax.mail.MessagingException; import org.opends.messages.Message; import org.opends.server.core.DirectoryServer; import org.opends.server.loggers.ErrorLogger; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.*; import org.opends.server.util.EMailMessage; import org.opends.server.util.StaticUtils; import org.opends.server.util.TimeThread; /** * 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 tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); // 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. private FailedDependencyAction failedDependencyAction; // 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. private LinkedList<String> dependencyIDs; // A set of log messages generated by this task. // TODO: convert from String to Message 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. private LinkedList<String> logMessages; // The set of e-mail addresses of the users to notify when the task is done // running, regardless of whether it completes successfully. private LinkedList<String> notifyOnCompletion; // The set of e-mail addresses of the users to notify if the task does not // complete successfully for some reason. private LinkedList<String> notifyOnError; // 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. private Operation operation; // The ID of the recurring task with which this task is associated. private String recurringTaskID; // 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; /** * Gets a message that identifies this type of task suitable for * presentation to humans in monitoring tools. * * @return name of task */ public Message 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 Message diaplay name */ public Message getAttributeDisplayName(String 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. */ public final void initializeTaskInternal(TaskScheduler taskScheduler, Entry taskEntry) throws InitializationException { this.taskScheduler = taskScheduler; this.taskEntry = taskEntry; this.taskEntryDN = taskEntry.getDN(); String taskDN = taskEntryDN.toString(); 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. taskID = getAttributeValue(ATTR_TASK_ID, false); recurringTaskID = getAttributeValue(ATTR_RECURRING_TASK_ID, false); if (taskID == null) { if (recurringTaskID == null) { Message message = ERR_TASK_MISSING_ATTR.get( String.valueOf(taskEntry.getDN()), ATTR_TASK_ID); throw new InitializationException(message); } else { 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); if (stateString == null) { taskState = TaskState.UNSCHEDULED; } else { taskState = TaskState.fromString(stateString); if (taskState == null) { Message message = ERR_TASK_INVALID_STATE.get(taskDN, stateString); 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; 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) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_TASK_CANNOT_PARSE_SCHEDULED_START_TIME.get(timeString, taskDN); throw new InitializationException(message, e); } } // 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) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_TASK_CANNOT_PARSE_ACTUAL_START_TIME.get(timeString, taskDN); throw new InitializationException(message, e); } } // 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) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_TASK_CANNOT_PARSE_COMPLETION_TIME.get(timeString, taskDN); throw new InitializationException(message, e); } } // Get information about any dependencies that the task might have. dependencyIDs = getAttributeValues(ATTR_TASK_DEPENDENCY_IDS); failedDependencyAction = FailedDependencyAction.CANCEL; String actionString = getAttributeValue(ATTR_TASK_FAILED_DEPENDENCY_ACTION, false); if (actionString != null) { failedDependencyAction = FailedDependencyAction.fromString(actionString); if (failedDependencyAction == null) { failedDependencyAction = FailedDependencyAction.defaultValue(); } } // 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) { logMessageCounter = logMessages.size(); } } /** * 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. */ private String getAttributeValue(String attributeName, boolean isRequired) throws InitializationException { List<Attribute> attrList = taskEntry.getAttribute(attributeName.toLowerCase()); if ((attrList == null) || attrList.isEmpty()) { if (isRequired) { Message message = ERR_TASK_MISSING_ATTR.get( String.valueOf(taskEntry.getDN()), attributeName); throw new InitializationException(message); } else { return null; } } if (attrList.size() > 1) { Message message = ERR_TASK_MULTIPLE_ATTRS_FOR_TYPE.get( attributeName, String.valueOf(taskEntry.getDN())); throw new InitializationException(message); } Iterator<AttributeValue> iterator = attrList.get(0).iterator(); if (! iterator.hasNext()) { if (isRequired) { Message message = ERR_TASK_NO_VALUES_FOR_ATTR.get( attributeName, String.valueOf(taskEntry.getDN())); throw new InitializationException(message); } else { return null; } } AttributeValue value = iterator.next(); if (iterator.hasNext()) { Message message = ERR_TASK_MULTIPLE_VALUES_FOR_ATTR.get( attributeName, String.valueOf(taskEntry.getDN())); throw new InitializationException(message); } return value.getValue().toString(); } /** * 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. */ private LinkedList<String> getAttributeValues(String attributeName) throws InitializationException { LinkedList<String> valueStrings = new LinkedList<String>(); List<Attribute> attrList = taskEntry.getAttribute(attributeName.toLowerCase()); if ((attrList == null) || attrList.isEmpty()) { return valueStrings; } if (attrList.size() > 1) { Message message = ERR_TASK_MULTIPLE_ATTRS_FOR_TYPE.get( attributeName, String.valueOf(taskEntry.getDN())); throw new InitializationException(message); } Iterator<AttributeValue> iterator = attrList.get(0).iterator(); while (iterator.hasNext()) { valueStrings.add(iterator.next().getValue().toString()); } 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. */ public final void setOperation(Operation operation) { this.operation = operation; } /** * Retrieves the unique identifier assigned to this task. * * @return The unique identifier assigned to this task. */ public final String getTaskID() { 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 && 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 * to disk. * * @param taskState The new state to use for the task. */ void setTaskState(TaskState taskState) { // We only need to grab the entry-level lock if we don't already hold the // broader scheduler lock. boolean needLock = (! taskScheduler.holdsSchedulerLock()); Lock lock = null; if (needLock) { lock = taskScheduler.writeLockEntry(taskEntryDN); } try { this.taskState = taskState; Attribute attr = Attributes.create(ATTR_TASK_STATE, taskState.toString()); ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(attr); taskEntry.putAttribute(attr.getAttributeType(), attrList); } finally { if (needLock) { taskScheduler.unlockEntry(taskEntryDN, lock); } } } /** * Sets a state for this task that is the result of a call to * {@link #interruptTask(TaskState, org.opends.messages.Message)}. * 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 */ protected void setTaskInterruptState(TaskState state) { 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.Message)}. * * @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.Message)} * 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. */ protected void replaceAttributeValue(String name, String value) throws DirectoryException { // We only need to grab the entry-level lock if we don't already hold the // broader scheduler lock. boolean needLock = (! taskScheduler.holdsSchedulerLock()); Lock lock = null; if (needLock) { lock = taskScheduler.writeLockEntry(taskEntryDN); } try { Entry taskEntry = getTaskEntry(); ArrayList<Modification> modifications = new ArrayList<Modification>(); modifications.add(new Modification(ModificationType.REPLACE, Attributes.create(name, value))); taskEntry.applyModifications(modifications); } finally { if (needLock) { taskScheduler.unlockEntry(taskEntryDN, lock); } } } /** * 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. boolean needLock = (! taskScheduler.holdsSchedulerLock()); Lock lock = null; if (needLock) { lock = taskScheduler.writeLockEntry(taskEntryDN); } try { this.actualStartTime = actualStartTime; Date d = new Date(actualStartTime); String startTimeStr = StaticUtils.formatDateTimeString(d); Attribute attr = Attributes.create(ATTR_TASK_ACTUAL_START_TIME, startTimeStr); ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(attr); taskEntry.putAttribute(attr.getAttributeType(), attrList); } finally { if (needLock) { taskScheduler.unlockEntry(taskEntryDN, lock); } } } /** * 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. boolean needLock = (! taskScheduler.holdsSchedulerLock()); Lock lock = null; if (needLock) { lock = taskScheduler.writeLockEntry(taskEntryDN); } try { this.completionTime = completionTime; SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); Date d = new Date(completionTime); Attribute attr = Attributes.create(ATTR_TASK_COMPLETION_TIME, dateFormat.format(d)); ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(attr); taskEntry.putAttribute(attr.getAttributeType(), attrList); } finally { if (needLock) { taskScheduler.unlockEntry(taskEntryDN, lock); } } } /** * 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. */ public final LinkedList<String> getDependencyIDs() { 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. */ public final LinkedList<String> getNotifyOnCompletionAddresses() { 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. */ public final LinkedList<String> getNotifyOnErrorAddresses() { 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. */ public final List<Message> getLogMessages() { List<Message> msgList = new ArrayList<Message>(); for(String logString : logMessages) { // TODO: a better job or recreating the message msgList.add(Message.raw(logString)); } return Collections.unmodifiableList(msgList); } /** * Writes a message to the error log using the provided information. * Tasks should use this method to log messages to the error log instead of * the one in <code>org.opends.server.loggers.Error</code> to ensure the * messages are included in the ds-task-log-message attribute. * * @param message The message to be logged. */ protected void logError(Message message) { // Simply pass this on to the server error logger, and it will call back // to the addLogMessage method for this task. ErrorLogger.logError(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 message he log message */ public void addLogMessage(Message message) { // 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. if (DirectoryServer.getSchema() == null) { return; } // We only need to grab the entry-level lock if we don't already hold the // broader scheduler lock. boolean needLock = (! taskScheduler.holdsSchedulerLock()); Lock lock = null; if (needLock) { lock = taskScheduler.writeLockEntry(taskEntryDN); } try { StringBuilder buffer = new StringBuilder(); buffer.append("["); buffer.append(TimeThread.getLocalTime()); buffer.append("] severity=\""); buffer.append(message.getDescriptor().getSeverity().name()); buffer.append("\" msgCount="); buffer.append(logMessageCounter++); buffer.append(" msgID="); buffer.append(message.getDescriptor().getId()); buffer.append(" message=\""); buffer.append(message.toString()); buffer.append("\""); String messageString = buffer.toString(); logMessages.add(messageString); AttributeType type = DirectoryServer.getAttributeType( ATTR_TASK_LOG_MESSAGES.toLowerCase()); if (type == null) { type = DirectoryServer.getDefaultAttributeType(ATTR_TASK_LOG_MESSAGES); } List<Attribute> attrList = taskEntry.getAttribute(type); AttributeValue value = AttributeValues.create(type, messageString); if (attrList == null) { attrList = new ArrayList<Attribute>(); attrList.add(Attributes.create(type, value)); taskEntry.putAttribute(type, attrList); } else if (attrList.isEmpty()) { attrList.add(Attributes.create(type, value)); } else { AttributeBuilder builder = new AttributeBuilder(attrList.get(0)); builder.add(value); attrList.set(0, builder.toAttribute()); } } finally { if (needLock) { taskScheduler.unlockEntry(taskEntryDN, lock); } } } /** * 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. */ public final int compareTo(Task task) { 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; } } else if (task.completionTime > 0) { // Completed tasks are always ordered before those that haven't completed. return 1; } 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; } } else if (task.actualStartTime > 0) { // 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. if (scheduledStartTime < task.scheduledStartTime) { return -1; } else if (scheduledStartTime > task.scheduledStartTime) { return 1; } else { return taskID.compareTo(task.taskID); } } /** * 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. */ public final TaskState execute() { setActualStartTime(TimeThread.getTime()); setTaskState(TaskState.RUNNING); taskScheduler.writeState(); TaskState taskState = this.taskState; try { taskState = runTask(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } taskState = TaskState.STOPPED_BY_ERROR; Message message = ERR_TASK_EXECUTE_FAILED.get( String.valueOf(taskEntry.getDN()), stackTraceToSingleLineString(e)); logError(message); } 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 (DirectoryServer.mailServerConfigured()) { LinkedHashSet<String> recipients = new LinkedHashSet<String>(); recipients.addAll(notifyOnCompletion); if (! TaskState.isSuccessful(taskState)) { recipients.addAll(notifyOnError); } if (! recipients.isEmpty()) { EMailMessage message = new EMailMessage(taskBackend.getNotificationSenderAddress(), new ArrayList<String>(recipients), taskState.toString() + " " + taskID); String scheduledStartDate; if (scheduledStartTime <= 0) { scheduledStartDate = ""; } else { scheduledStartDate = new Date(scheduledStartTime).toString(); } String actualStartDate = new Date(actualStartTime).toString(); String completionDate = new Date(completionTime).toString(); message.setBody(INFO_TASK_COMPLETION_BODY.get(taskID, String.valueOf(taskState), scheduledStartDate, actualStartDate, completionDate)); for (String logMessage : logMessages) { message.appendToBody(logMessage); message.appendToBody("\r\n"); } message.send(); } } } /** * 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. */ 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 * {@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. */ public void interruptTask(TaskState interruptState, Message interruptReason) { // 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; } }