mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
31.30.2007 a0c742dc589e56e8b1a7498de1d165e039f58aac
Update the task backend to provide a mechanism for sending e-mail messages to
notify administrators whenever a given task has been completed. It is possible
to specify a set of administrators that should be notified only if the task
does not complete successfully, and/or a set of administrators that should be
notified regardless of the task's success or failure. The basic framework for
this capability has always been in place, and this change only provides the
final implementation that actually generates and sends the e-mail message.

This also includes a change to the way that log messages are handled so that
any messages logged by a thread executing a task (or any DirectoryThread
subclass created by a task thread) will be captured as part of that task.
These messages will also be included in the notification e-mail message.

OpenDS Issue Numbers: 2033, 2034
9 files modified
347 ■■■■ changed files
opendj-sdk/opends/resource/schema/02-config.ldif 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/TaskBackendConfiguration.xml 35 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java 125 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java 50 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java 12 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskThread.java 50 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java 15 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/loggers/ErrorLogger.java 34 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java 19 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1553,6 +1553,10 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.464 NAME 'ds-rlim-idle-time-limit'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE USAGE directoryOperation
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.465
  NAME 'ds-cfg-notification-sender-address'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1622,7 +1626,8 @@
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.10 NAME 'ds-cfg-task-backend'
  SUP ds-cfg-backend STRUCTURAL MAY ( ds-cfg-task-backing-file $
  ds-cfg-task-retention-time ) X-ORIGIN 'OpenDS Directory Server' )
  ds-cfg-task-retention-time $ ds-cfg-notification-sender-address )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.11 NAME 'ds-cfg-branch'
  SUP top STRUCTURAL MUST cn X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.12
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/TaskBackendConfiguration.xml
@@ -31,6 +31,7 @@
  extends="backend"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The task backend provides a mechanism for processing tasks in the
    OpenDS Directory Server. Tasks are intended to provide access to certain
@@ -51,6 +52,7 @@
    The org.opends.server.backends.task.TaskBackend class provides the entry
    point for the task backend implementation.
  </adm:description>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.10</ldap:oid>
@@ -58,6 +60,7 @@
      <ldap:superior>ds-cfg-backend</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="backend-class">
    <adm:default-behavior>
      <adm:defined>
@@ -67,6 +70,7 @@
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="task-backing-file"
    mandatory="true"
    multi-valued="false">
@@ -86,6 +90,7 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="task-retention-time"
    mandatory="false"
    multi-valued="false">
@@ -108,4 +113,34 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="notification-sender-address"
  mandatory="false"
  multi-valued="false">
    <adm:synopsis>
      This specifies the e-mail address to use as the sender (i.e., "From:")
      address for notification mail messages generated when a task completes
      execution.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          The default sender address used will be "opends-task-notification@"
          followed by the canonical address of the system on which the server is
          running.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.465</ldap:oid>
        <ldap:name>ds-cfg-notification-sender-address</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-sdk/opends/src/server/org/opends/server/backends/task/Task.java
@@ -38,12 +38,16 @@
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import javax.mail.MessagingException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -51,16 +55,13 @@
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Operation;
import org.opends.server.util.EMailMessage;
import org.opends.server.util.TimeThread;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import org.opends.server.messages.MessageHandler;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -125,6 +126,9 @@
  // 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;
@@ -153,6 +157,7 @@
    String taskDN = taskEntryDN.toString();
    taskBackend       = taskScheduler.getTaskBackend();
    logMessageCounter = 0;
@@ -787,6 +792,8 @@
    return logMessages;
  }
  /**
   * 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
@@ -800,12 +807,11 @@
   * @param  errorID   The error ID that uniquely identifies the provided format
   *                   string.
   */
  protected void logError(ErrorLogCategory category,
                              ErrorLogSeverity severity, int errorID)
  protected void logError(ErrorLogCategory category, ErrorLogSeverity severity,
                          int errorID)
  {
    String message = MessageHandler.getMessage(errorID);
    addLogMessage(severity, errorID, message);
    // Simply pass this on to the server error logger, and it will call back
    // to the addLogMessage method for this task.
    ErrorLogger.logError(category, severity, errorID);
  }
@@ -826,13 +832,11 @@
   * @param  args      The set of arguments to use for the provided format
   *                   string.
   */
  protected void logError(ErrorLogCategory category,
                              ErrorLogSeverity severity, int errorID,
                              Object... args)
  protected void logError(ErrorLogCategory category, ErrorLogSeverity severity,
                          int errorID, Object... args)
  {
    String message = MessageHandler.getMessage(errorID);
    addLogMessage(severity, errorID, message);
    // Simply pass this on to the server error logger, and it will call back
    // to the addLogMessage method for this task.
    ErrorLogger.logError(category, severity, errorID,args);
  }
@@ -852,26 +856,27 @@
   * @param  errorID   The error ID that uniquely identifies the format string
   *                   used to generate the provided message.
   */
  protected void logError(ErrorLogCategory category,
                              ErrorLogSeverity severity, String message,
                              int errorID)
  protected void logError(ErrorLogCategory category, ErrorLogSeverity severity,
                          String message, int errorID)
  {
    addLogMessage(severity, errorID, message);
    ErrorLogger.logError(category, severity, message,
        errorID);
    // Simply pass this on to the server error logger, and it will call back
    // to the addLogMessage method for this task.
    ErrorLogger.logError(category, severity, message, errorID);
  }
  /**
   * 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 logError methods in this class. It does not
   * indirectly through the {@code ErrorLog.logError} methods. It does not
   * automatically persist the updated task information to disk.
   *
   * @param  severity       The severity level for the log message.
   * @param  messageID      The ID that uniquely identifies the log message.
   * @param  messageString  The text of the log message
   */
  void addLogMessage(ErrorLogSeverity severity, int messageID,
  public void addLogMessage(ErrorLogSeverity severity, int messageID,
                     String messageString)
  {
    Lock lock = taskScheduler.writeLockEntry(taskEntryDN);
@@ -1067,8 +1072,8 @@
      int    msgID   = MSGID_TASK_EXECUTE_FAILED;
      String message = getMessage(msgID, String.valueOf(taskEntry.getDN()),
                                  stackTraceToSingleLineString(e));
      logError(ErrorLogCategory.TASK, ErrorLogSeverity.SEVERE_ERROR, message,
               msgID);
      ErrorLogger.logError(ErrorLogCategory.TASK, ErrorLogSeverity.SEVERE_ERROR,
                           message, msgID);
    }
    finally
    {
@@ -1076,13 +1081,81 @@
      taskScheduler.writeState();
    }
    // FIXME -- Send an e-mail message if appropriate.
    try
    {
      sendNotificationEMailMessage();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, 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.
   */
  private 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(getMessage(MSGID_TASK_COMPLETION_BODY, 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
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -29,6 +29,7 @@
import java.io.File;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -116,6 +117,9 @@
  // removed from the set of scheduled tasks.
  private long retentionTime;
  // The e-mail address to use for the sender from notification messages.
  private String notificationSenderAddress;
  // The path to the task backing file.
  private String taskBackingFile;
@@ -226,6 +230,22 @@
    retentionTime = cfg.getTaskRetentionTime();
    // Get the notification sender address.
    notificationSenderAddress = cfg.getNotificationSenderAddress();
    if (notificationSenderAddress == null)
    {
      try
      {
        notificationSenderAddress = "opends-task-notification@" +
             InetAddress.getLocalHost().getCanonicalHostName();
      }
      catch (Exception e)
      {
        notificationSenderAddress = "opends-task-notification@opends.org";
      }
    }
    // Get the path to the task data backing file.
    taskBackingFile = cfg.getTaskBackingFile();
@@ -1285,6 +1305,22 @@
    }
    String tmpNotificationAddress = configEntry.getNotificationSenderAddress();
    if (tmpNotificationAddress == null)
    {
      try
      {
        tmpNotificationAddress = "opends-task-notification@" +
             InetAddress.getLocalHost().getCanonicalHostName();
      }
      catch (Exception e)
      {
        tmpNotificationAddress = "opends-task-notification@opends.org";
      }
    }
    notificationSenderAddress = tmpNotificationAddress;
    currentConfig = configEntry;
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
@@ -1319,6 +1355,20 @@
  /**
   * Retrieves the sender address that should be used for e-mail notifications
   * of task completion.
   *
   * @return  The sender address that should be used for e-mail notifications of
   *          task completion.
   */
  public String getNotificationSenderAddress()
  {
    return notificationSenderAddress;
  }
  /**
   * Retrieves the length of time in seconds that information for a task should
   * be retained after processing on it has completed.
   *
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1357,6 +1357,18 @@
  /**
   * Retrieves the task backend with which this scheduler is associated.
   *
   * @return  The task backend with which this scheduler is associated.
   */
  public TaskBackend getTaskBackend()
  {
    return taskBackend;
  }
  /**
   * Retrieves the root entry that is the common ancestor for all entries in the
   * task backend.
   *
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskThread.java
@@ -29,11 +29,13 @@
import org.opends.server.api.DirectoryThread;
import org.opends.server.types.ErrorLogSeverity;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.BackendMessages.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.StaticUtils.*;
@@ -41,6 +43,7 @@
import java.util.Map;
/**
 * This class defines a thread that will be used to execute a scheduled task
 * within the server and provide appropriate notification that the task is
@@ -56,16 +59,12 @@
  // Indicates whether a request has been made for this thread to exit.
  private boolean exitRequested;
  // The thread ID for this task thread.
  private int threadID;
  // The task currently being processed by this thread.
  private Task task;
  // The reference to the scheduler with which this thread is associated.
  private TaskScheduler taskScheduler;
@@ -90,9 +89,10 @@
    this.taskScheduler = taskScheduler;
    this.threadID      = threadID;
    task          = null;
    notifyLock    = new Object();
    exitRequested = false;
    setAssociatedTask(null);
  }
@@ -106,7 +106,7 @@
   */
  public Task getTask()
  {
    return task;
    return getAssociatedTask();
  }
@@ -119,7 +119,7 @@
   */
  public void setTask(Task task)
  {
    this.task = task;
    setAssociatedTask(task);
    synchronized (notifyLock)
    {
@@ -142,11 +142,11 @@
  public void interruptTask(TaskState interruptState, String interruptReason,
                            boolean exitThread)
  {
    if (task != null)
    if (getAssociatedTask() != null)
    {
      try
      {
        task.interruptTask(interruptState, interruptReason);
        getAssociatedTask().interruptTask(interruptState, interruptReason);
      }
      catch (Exception e)
      {
@@ -173,7 +173,7 @@
  {
    while (! exitRequested)
    {
      if (task == null)
      if (getAssociatedTask() == null)
      {
        try
        {
@@ -195,8 +195,8 @@
      try
      {
        TaskState returnState = task.execute();
        task.setTaskState(returnState);
        TaskState returnState = getAssociatedTask().execute();
        getAssociatedTask().setTaskState(returnState);
      }
      catch (Exception e)
      {
@@ -205,17 +205,20 @@
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Task task = getAssociatedTask();
        int    msgID   = MSGID_TASK_EXECUTE_FAILED;
        String message = getMessage(msgID,
                                    String.valueOf(task.getTaskEntry().getDN()),
                                    stackTraceToSingleLineString(e));
        task.addLogMessage(ErrorLogSeverity.FATAL_ERROR, msgID, message);
        logError(ErrorLogCategory.TASK, ErrorLogSeverity.FATAL_ERROR, message,
                 msgID);
        task.setTaskState(TaskState.STOPPED_BY_ERROR);
      }
      Task completedTask = task;
      task = null;
      Task completedTask = getAssociatedTask();
      setAssociatedTask(null);
      if (! taskScheduler.threadDone(this, completedTask))
      {
        exitRequested = true;
@@ -223,13 +226,16 @@
      }
    }
    if (task != null)
    if (getAssociatedTask() != null)
    {
      Task task = getAssociatedTask();
      task.setTaskState(TaskState.STOPPED_BY_SHUTDOWN);
      taskScheduler.threadDone(this, task);
    }
  }
  /**
   * Retrieves any relevent debug information with which this tread is
   * associated so they can be included in debug messages.
@@ -239,7 +245,11 @@
  public Map<String, String> getDebugProperties()
  {
    Map<String, String> properties = super.getDebugProperties();
    properties.put("task", task.toString());
    if (getAssociatedTask() != null)
    {
      properties.put("task", getAssociatedTask().toString());
    }
    return properties;
  }
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2058,6 +2058,21 @@
  /**
   * Indicates whether the Directory Server is configured with information about
   * one or more mail servers and may therefore be used to send e-mail messages.
   *
   * @return  {@code true} if the Directory Server is configured to be able to
   *          send e-mail messages, or {@code false} if not.
   */
  public static boolean mailServerConfigured()
  {
    return ((directoryServer.mailServerPropertySets != null) &&
            (! directoryServer.mailServerPropertySets.isEmpty()));
  }
  /**
   * Specifies the set of mail server properties that should be used for SMTP
   * communication.
   *
opendj-sdk/opends/src/server/org/opends/server/loggers/ErrorLogger.java
@@ -34,7 +34,10 @@
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import org.opends.server.api.DirectoryThread;
import org.opends.server.api.ErrorLogPublisher;
import org.opends.server.backends.task.Task;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.messages.MessageHandler;
import org.opends.server.types.*;
import org.opends.server.admin.std.server.ErrorLogPublisherCfg;
@@ -47,7 +50,6 @@
import org.opends.server.core.DirectoryServer;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.messages.MessageHandler.getMessage;
@@ -441,6 +443,16 @@
    {
      publisher.logError(category, severity, message, errorID);
    }
    if (Thread.currentThread() instanceof DirectoryThread)
    {
      DirectoryThread thread = (DirectoryThread) Thread.currentThread();
      Task task = thread.getAssociatedTask();
      if (task != null)
      {
        task.addLogMessage(severity, errorID, message);
      }
    }
  }
@@ -467,6 +479,16 @@
    {
      publisher.logError(category, severity, message, errorID);
    }
    if (Thread.currentThread() instanceof DirectoryThread)
    {
      DirectoryThread thread = (DirectoryThread) Thread.currentThread();
      Task task = thread.getAssociatedTask();
      if (task != null)
      {
        task.addLogMessage(severity, errorID, message);
      }
    }
  }
@@ -490,6 +512,16 @@
    {
      publisher.logError(category, severity, message, errorID);
    }
    if (Thread.currentThread() instanceof DirectoryThread)
    {
      DirectoryThread thread = (DirectoryThread) Thread.currentThread();
      Task task = thread.getAssociatedTask();
      if (task != null)
      {
        task.addLogMessage(severity, errorID, message);
      }
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3237,6 +3237,17 @@
  /**
   * The message ID for the message that will be used as the body of the
   * notification e-mail message sent when a task is completed.  It takes
   * five arguments, which are string representations of the task ID, task
   * state, scheduled start date, actual start date, and completion date.
   */
  public static final int MSGID_TASK_COMPLETION_BODY =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_INFORMATIONAL | 299;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -3993,6 +4004,14 @@
    registerMessage(MSGID_TASK_EXECUTE_FAILED,
                    "An error occurred while executing the task defined in " +
                    "entry %s:  %s");
    registerMessage(MSGID_TASK_COMPLETION_BODY,
                    "Task ID:  %s\r\n" +
                    "Task State:  %s\r\n" +
                    "Scheduled Start Time:  %s\r\n" +
                    "Actual Start Time:  %s\r\n" +
                    "Completion Time:  %s\r\n" +
                    "\r\n" +
                    "Log Messages:\r\n");
    registerMessage(MSGID_RECURRINGTASK_NO_ID_ATTRIBUTE,