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

jcduff
23.04.2008 f73b655466092169abac34833fb628fce1fcdebe
opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
@@ -25,17 +25,27 @@
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 */
package org.opends.server.loggers;
import org.opends.messages.Message;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
import org.opends.server.admin.std.server.AccessLogPublisherCfg;
import org.opends.server.api.*;
import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
import org.opends.server.api.AccessLogPublisher;
import org.opends.server.api.ClientConnection;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AbandonOperation;
import org.opends.server.core.AddOperation;
@@ -44,48 +54,59 @@
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.UnbindOperation;
import org.opends.server.types.*;
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.FilePermission;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Operation;
import org.opends.server.types.ResultCode;
import org.opends.server.util.TimeThread;
import static org.opends.messages.ConfigMessages.*;
import org.opends.messages.MessageBuilder;
import static org.opends.server.util.StaticUtils.getFileForPath;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
/**
 * This class provides the implementation of the access logger used by
 * the directory server.
 */
public class TextAccessLogPublisher
    extends AccessLogPublisher<FileBasedAccessLogPublisherCfg>
    implements ConfigurationChangeListener<FileBasedAccessLogPublisherCfg>
public class TextAccessLogPublisher extends
    AccessLogPublisher<FileBasedAccessLogPublisherCfg> implements
    ConfigurationChangeListener<FileBasedAccessLogPublisherCfg>
{
  private boolean suppressInternalOperations = true;
  private boolean suppressSynchronizationOperations = false;
  private TextWriter writer;
  private FileBasedAccessLogPublisherCfg currentConfig;
  /**
   * Returns an instance of the text access log publisher that will print
   * all messages to the provided writer. This is used to print the messages
   * to the console when the server starts up.
   *
   * @param writer The text writer where the message will be written to.
   * @param suppressInternal Indicates whether to suppress internal operations.
   * @return The instance of the text error log publisher that will print
   * all messages to standard out.
   * The category to use when logging responses.
   */
  public static TextAccessLogPublisher
      getStartupTextAccessPublisher(TextWriter writer, boolean suppressInternal)
  private static final String CATEGORY_RESPONSE = "RES";
  /**
   * The category to use when logging requests.
   */
  private static final String CATEGORY_REQUEST = "REQ";
  /**
   * Returns an instance of the text access log publisher that will
   * print all messages to the provided writer. This is used to print
   * the messages to the console when the server starts up.
   *
   * @param writer
   *          The text writer where the message will be written to.
   * @param suppressInternal
   *          Indicates whether to suppress internal operations.
   * @return The instance of the text error log publisher that will
   *         print all messages to standard out.
   */
  public static TextAccessLogPublisher getStartupTextAccessPublisher(
      TextWriter writer, boolean suppressInternal)
  {
    TextAccessLogPublisher startupPublisher = new TextAccessLogPublisher();
    startupPublisher.writer = writer;
@@ -94,17 +115,151 @@
    return startupPublisher;
  }
  private FileBasedAccessLogPublisherCfg currentConfig;
  private boolean suppressInternalOperations = true;
  private boolean suppressSynchronizationOperations = false;
  private TextWriter writer;
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAcceptable(AccessLogPublisherCfg configuration,
                                           List<Message> unacceptableReasons)
  public ConfigChangeResult applyConfigurationChange(
      FileBasedAccessLogPublisherCfg config)
  {
    FileBasedAccessLogPublisherCfg config =
        (FileBasedAccessLogPublisherCfg) configuration;
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    suppressInternalOperations = config.isSuppressInternalOperations();
    suppressSynchronizationOperations = config
        .isSuppressSynchronizationOperations();
    File logFile = getFileForPath(config.getLogFile());
    FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
    try
    {
      FilePermission perm = FilePermission.decodeUNIXMode(config
          .getLogFilePermissions());
      boolean writerAutoFlush = config.isAutoFlush()
          && !config.isAsynchronous();
      TextWriter currentWriter;
      // Determine the writer we are using. If we were writing
      // asynchronously, we need to modify the underlying writer.
      if (writer instanceof AsyncronousTextWriter)
      {
        currentWriter = ((AsyncronousTextWriter) writer).getWrappedWriter();
      }
      else
      {
        currentWriter = writer;
      }
      if (currentWriter instanceof MultifileTextWriter)
      {
        MultifileTextWriter mfWriter = (MultifileTextWriter) currentWriter;
        mfWriter.setNamingPolicy(fnPolicy);
        mfWriter.setFilePermissions(perm);
        mfWriter.setAppend(config.isAppend());
        mfWriter.setAutoFlush(writerAutoFlush);
        mfWriter.setBufferSize((int) config.getBufferSize());
        mfWriter.setInterval(config.getTimeInterval());
        mfWriter.removeAllRetentionPolicies();
        mfWriter.removeAllRotationPolicies();
        for (DN dn : config.getRotationPolicyDNs())
        {
          mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
        }
        for (DN dn : config.getRetentionPolicyDNs())
        {
          mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
        }
        if (writer instanceof AsyncronousTextWriter && !config.isAsynchronous())
        {
          // The asynchronous setting is being turned off.
          AsyncronousTextWriter asyncWriter = ((AsyncronousTextWriter) writer);
          writer = mfWriter;
          asyncWriter.shutdown(false);
        }
        if (!(writer instanceof AsyncronousTextWriter)
            && config.isAsynchronous())
        {
          // The asynchronous setting is being turned on.
          AsyncronousTextWriter asyncWriter = new AsyncronousTextWriter(
              "Asyncronous Text Writer for " + config.dn().toNormalizedString(),
              config.getQueueSize(), config.isAutoFlush(), mfWriter);
          writer = asyncWriter;
        }
        if ((currentConfig.isAsynchronous() && config.isAsynchronous())
            && (currentConfig.getQueueSize() != config.getQueueSize()))
        {
          adminActionRequired = true;
        }
        currentConfig = config;
      }
    }
    catch (Exception e)
    {
      Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(config.dn()
          .toString(), stackTraceToSingleLineString(e));
      resultCode = DirectoryServer.getServerErrorResultCode();
      messages.add(message);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public void close()
  {
    writer.shutdown();
    if (currentConfig != null)
    {
      currentConfig.removeFileBasedAccessChangeListener(this);
    }
  }
  /**
   * {@inheritDoc}
   */
  public DN getDN()
  {
    if (currentConfig != null)
    {
      return currentConfig.dn();
    }
    else
    {
      return null;
    }
  }
  /**
   * {@inheritDoc}
   */
@@ -117,68 +272,60 @@
    try
    {
      FilePermission perm =
          FilePermission.decodeUNIXMode(config.getLogFilePermissions());
      FilePermission perm = FilePermission.decodeUNIXMode(config
          .getLogFilePermissions());
      LogPublisherErrorHandler errorHandler =
          new LogPublisherErrorHandler(config.dn());
      LogPublisherErrorHandler errorHandler = new LogPublisherErrorHandler(
          config.dn());
      boolean writerAutoFlush =
          config.isAutoFlush() && !config.isAsynchronous();
      boolean writerAutoFlush = config.isAutoFlush()
          && !config.isAsynchronous();
      MultifileTextWriter writer =
          new MultifileTextWriter("Multifile Text Writer for " +
              config.dn().toNormalizedString(),
                                  config.getTimeInterval(),
                                  fnPolicy,
                                  perm,
                                  errorHandler,
                                  "UTF-8",
                                  writerAutoFlush,
                                  config.isAppend(),
                                  (int)config.getBufferSize());
      MultifileTextWriter writer = new MultifileTextWriter(
          "Multifile Text Writer for " + config.dn().toNormalizedString(),
          config.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8",
          writerAutoFlush, config.isAppend(), (int) config.getBufferSize());
      // Validate retention and rotation policies.
      for(DN dn : config.getRotationPolicyDNs())
      for (DN dn : config.getRotationPolicyDNs())
      {
        writer.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
      }
      for(DN dn: config.getRetentionPolicyDNs())
      for (DN dn : config.getRetentionPolicyDNs())
      {
        writer.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
      }
      if(config.isAsynchronous())
      if (config.isAsynchronous())
      {
        this.writer = new AsyncronousTextWriter("Asyncronous Text Writer for " +
            config.dn().toNormalizedString(), config.getQueueSize(),
                                              config.isAutoFlush(),
                                              writer);
        this.writer = new AsyncronousTextWriter("Asyncronous Text Writer for "
            + config.dn().toNormalizedString(), config.getQueueSize(), config
            .isAutoFlush(), writer);
      }
      else
      {
        this.writer = writer;
      }
    }
    catch(DirectoryException e)
    catch (DirectoryException e)
    {
      Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(
          config.dn().toString(), String.valueOf(e));
      Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(config.dn()
          .toString(), String.valueOf(e));
      throw new InitializationException(message, e);
    }
    catch(IOException e)
    catch (IOException e)
    {
      Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(
          logFile.toString(), config.dn().toString(), String.valueOf(e));
      Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get(logFile
          .toString(), config.dn().toString(), String.valueOf(e));
      throw new InitializationException(message, e);
    }
    suppressInternalOperations = config.isSuppressInternalOperations();
    suppressSynchronizationOperations =
      config.isSuppressSynchronizationOperations();
    suppressSynchronizationOperations = config
        .isSuppressSynchronizationOperations();
    currentConfig = config;
@@ -190,160 +337,469 @@
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
       FileBasedAccessLogPublisherCfg config, List<Message> unacceptableReasons)
   {
     // Make sure the permission is valid.
     try
     {
       FilePermission filePerm =
           FilePermission.decodeUNIXMode(config.getLogFilePermissions());
       if(!filePerm.isOwnerWritable())
       {
         Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(
             config.getLogFilePermissions());
         unacceptableReasons.add(message);
         return false;
       }
     }
     catch(DirectoryException e)
     {
       Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(
               config.getLogFilePermissions(), String.valueOf(e));
       unacceptableReasons.add(message);
       return false;
     }
     return true;
   }
  /**
   * {@inheritDoc}
   */
   public ConfigChangeResult applyConfigurationChange(
       FileBasedAccessLogPublisherCfg config)
   {
     // Default result code.
     ResultCode resultCode = ResultCode.SUCCESS;
     boolean adminActionRequired = false;
     ArrayList<Message> messages = new ArrayList<Message>();
     suppressInternalOperations = config.isSuppressInternalOperations();
     suppressSynchronizationOperations =
       config.isSuppressSynchronizationOperations();
     File logFile = getFileForPath(config.getLogFile());
     FileNamingPolicy fnPolicy = new TimeStampNaming(logFile);
     try
     {
       FilePermission perm =
           FilePermission.decodeUNIXMode(config.getLogFilePermissions());
       boolean writerAutoFlush =
          config.isAutoFlush() && !config.isAsynchronous();
       TextWriter currentWriter;
       // Determine the writer we are using. If we were writing asyncronously,
       // we need to modify the underlaying writer.
       if(writer instanceof AsyncronousTextWriter)
       {
         currentWriter = ((AsyncronousTextWriter)writer).getWrappedWriter();
       }
       else
       {
         currentWriter = writer;
       }
       if(currentWriter instanceof MultifileTextWriter)
       {
         MultifileTextWriter mfWriter = (MultifileTextWriter)currentWriter;
         mfWriter.setNamingPolicy(fnPolicy);
         mfWriter.setFilePermissions(perm);
         mfWriter.setAppend(config.isAppend());
         mfWriter.setAutoFlush(writerAutoFlush);
         mfWriter.setBufferSize((int)config.getBufferSize());
         mfWriter.setInterval(config.getTimeInterval());
         mfWriter.removeAllRetentionPolicies();
         mfWriter.removeAllRotationPolicies();
         for(DN dn : config.getRotationPolicyDNs())
         {
           mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn));
         }
         for(DN dn: config.getRetentionPolicyDNs())
         {
           mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn));
         }
         if(writer instanceof AsyncronousTextWriter && !config.isAsynchronous())
         {
           // The asynronous setting is being turned off.
           AsyncronousTextWriter asyncWriter = ((AsyncronousTextWriter)writer);
           writer = mfWriter;
           asyncWriter.shutdown(false);
         }
         if(!(writer instanceof AsyncronousTextWriter) &&
             config.isAsynchronous())
         {
           // The asynronous setting is being turned on.
           AsyncronousTextWriter asyncWriter =
               new AsyncronousTextWriter("Asyncronous Text Writer for " +
                   config.dn().toNormalizedString(), config.getQueueSize(),
                                                     config.isAutoFlush(),
                                                     mfWriter);
           writer = asyncWriter;
         }
         if((currentConfig.isAsynchronous() && config.isAsynchronous()) &&
             (currentConfig.getQueueSize() != config.getQueueSize()))
         {
           adminActionRequired = true;
         }
         currentConfig = config;
       }
     }
     catch(Exception e)
     {
       Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(
               config.dn().toString(),
               stackTraceToSingleLineString(e));
       resultCode = DirectoryServer.getServerErrorResultCode();
       messages.add(message);
     }
     return new ConfigChangeResult(resultCode, adminActionRequired, messages);
   }
  /**
   * {@inheritDoc}
   */
  public void close()
  public boolean isConfigurationAcceptable(
      AccessLogPublisherCfg configuration,
      List<Message> unacceptableReasons)
  {
    writer.shutdown();
    if(currentConfig != null)
    {
      currentConfig.removeFileBasedAccessChangeListener(this);
    }
    FileBasedAccessLogPublisherCfg config =
      (FileBasedAccessLogPublisherCfg) configuration;
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
  }
  /**
   * Writes a message to the access logger with information about a new client
   * connection that has been established, regardless of whether it will be
   * immediately terminated.
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      FileBasedAccessLogPublisherCfg config, List<Message> unacceptableReasons)
  {
    // Make sure the permission is valid.
    try
    {
      FilePermission filePerm = FilePermission.decodeUNIXMode(config
          .getLogFilePermissions());
      if (!filePerm.isOwnerWritable())
      {
        Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(config
            .getLogFilePermissions());
        unacceptableReasons.add(message);
        return false;
      }
    }
    catch (DirectoryException e)
    {
      Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get(config
          .getLogFilePermissions(), String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public void logAbandonIntermediateMessage(AbandonOperation abandonOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(abandonOperation, "ABANDON", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * abandon request associated with the provided abandon operation.
   *
   * @param  clientConnection  The client connection that has been established.
   * @param abandonOperation
   *          The abandon operation containing the information to use
   *          to log the abandon request.
   */
  public void logAbandonRequest(AbandonOperation abandonOperation)
  {
    if (!isLoggable(abandonOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(abandonOperation, "ABANDON", CATEGORY_REQUEST, buffer);
    buffer.append(" idToAbandon=");
    buffer.append(abandonOperation.getIDToAbandon());
    if (abandonOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * result of the provided abandon operation.
   *
   * @param abandonOperation
   *          The abandon operation containing the information to use
   *          to log the abandon request.
   */
  public void logAbandonResult(AbandonOperation abandonOperation)
  {
    if (!isLoggable(abandonOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(abandonOperation, "ABANDON", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(abandonOperation.getResultCode().getIntValue());
    MessageBuilder msg = abandonOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = abandonOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    buffer.append(" etime=");
    buffer.append(abandonOperation.getProcessingTime());
    writer.writeRecord(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  public void logAddIntermediateMessage(AddOperation addOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(addOperation, "ADD", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * add request associated with the provided add operation.
   *
   * @param addOperation
   *          The add operation containing the information to use to
   *          log the add request.
   */
  public void logAddRequest(AddOperation addOperation)
  {
    if (!isLoggable(addOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(addOperation, "ADD", CATEGORY_REQUEST, buffer);
    buffer.append(" dn=\"");
    addOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (addOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * add response associated with the provided add operation.
   *
   * @param addOperation
   *          The add operation containing the information to use to
   *          log the add response.
   */
  public void logAddResponse(AddOperation addOperation)
  {
    if (!isLoggable(addOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(addOperation, "ADD", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(addOperation.getResultCode().getIntValue());
    MessageBuilder msg = addOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = addOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    DN proxiedAuthDN = addOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append('\"');
    }
    buffer.append(" etime=");
    buffer.append(addOperation.getProcessingTime());
    writer.writeRecord(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  public void logBindIntermediateMessage(BindOperation bindOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(bindOperation, "BIND", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * bind request associated with the provided bind operation.
   *
   * @param bindOperation
   *          The bind operation with the information to use to log
   *          the bind request.
   */
  public void logBindRequest(BindOperation bindOperation)
  {
    if (!isLoggable(bindOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(bindOperation, "BIND", CATEGORY_REQUEST, buffer);
    switch (bindOperation.getAuthenticationType())
    {
    case SIMPLE:
      buffer.append(" type=SIMPLE");
      break;
    case SASL:
      buffer.append(" type=SASL mechanism=");
      buffer.append(bindOperation.getSASLMechanism());
      break;
    default:
      buffer.append(" type=");
      buffer.append(bindOperation.getAuthenticationType());
      break;
    }
    buffer.append(" dn=\"");
    bindOperation.getRawBindDN().toString(buffer);
    buffer.append("\"");
    if (bindOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * bind response associated with the provided bind operation.
   *
   * @param bindOperation
   *          The bind operation containing the information to use to
   *          log the bind response.
   */
  public void logBindResponse(BindOperation bindOperation)
  {
    if (!isLoggable(bindOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(bindOperation, "BIND", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(bindOperation.getResultCode().getIntValue());
    MessageBuilder msg = bindOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    Message failureMessage = bindOperation.getAuthFailureReason();
    if (failureMessage != null)
    {
      buffer.append(" authFailureID=");
      buffer.append(failureMessage.getDescriptor().getId());
      buffer.append(" authFailureReason=\"");
      buffer.append(failureMessage);
      buffer.append('\"');
    }
    msg = bindOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    if (bindOperation.getResultCode() == ResultCode.SUCCESS)
    {
      AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo();
      if (authInfo != null)
      {
        DN authDN = authInfo.getAuthenticationDN();
        if (authDN != null)
        {
          buffer.append(" authDN=\"");
          authDN.toString(buffer);
          buffer.append('\"');
          DN authzDN = authInfo.getAuthorizationDN();
          if (!authDN.equals(authzDN))
          {
            buffer.append(" authzDN=\"");
            if (authzDN != null)
            {
              authzDN.toString(buffer);
            }
            buffer.append('\"');
          }
        }
        else
        {
          buffer.append(" authDN=\"\"");
        }
      }
    }
    buffer.append(" etime=");
    long etime = bindOperation.getProcessingNanoTime();
    if (etime <= -1)
    {
      etime = bindOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  public void logCompareIntermediateMessage(CompareOperation compareOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(compareOperation, "COMPARE", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * compare request associated with the provided compare operation.
   *
   * @param compareOperation
   *          The compare operation containing the information to use
   *          to log the compare request.
   */
  public void logCompareRequest(CompareOperation compareOperation)
  {
    if (!isLoggable(compareOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(compareOperation, "COMPARE", CATEGORY_REQUEST, buffer);
    buffer.append(" dn=\"");
    compareOperation.getRawEntryDN().toString(buffer);
    buffer.append("\" attr=");
    buffer.append(compareOperation.getAttributeType());
    if (compareOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * compare response associated with the provided compare operation.
   *
   * @param compareOperation
   *          The compare operation containing the information to use
   *          to log the compare response.
   */
  public void logCompareResponse(CompareOperation compareOperation)
  {
    if (!isLoggable(compareOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(compareOperation, "COMPARE", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(compareOperation.getResultCode().getIntValue());
    MessageBuilder msg = compareOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = compareOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    DN proxiedAuthDN = compareOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append('\"');
    }
    buffer.append(" etime=");
    long etime = compareOperation.getProcessingNanoTime();
    if (etime <= -1)
    {
      etime = compareOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about a
   * new client connection that has been established, regardless of
   * whether it will be immediately terminated.
   *
   * @param clientConnection
   *          The client connection that has been established.
   */
  public void logConnect(ClientConnection clientConnection)
  {
@@ -360,9 +816,9 @@
    buffer.append(" CONNECT conn=");
    buffer.append(connectionID);
    buffer.append(" from=");
    buffer.append(clientConnection.getClientAddress());
    buffer.append(clientConnection.getClientHostPort());
    buffer.append(" to=");
    buffer.append(clientConnection.getServerAddress());
    buffer.append(clientConnection.getServerHostPort());
    buffer.append(" protocol=");
    buffer.append(clientConnection.getProtocol());
@@ -371,19 +827,118 @@
  }
  /**
   * {@inheritDoc}
   */
  public void logDeleteIntermediateMessage(DeleteOperation deleteOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(deleteOperation, "DELETE", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * delete request associated with the provided delete operation.
   *
   * @param deleteOperation
   *          The delete operation with the information to use to log
   *          the delete request.
   */
  public void logDeleteRequest(DeleteOperation deleteOperation)
  {
    if (!isLoggable(deleteOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(deleteOperation, "DELETE", CATEGORY_REQUEST, buffer);
    buffer.append(" dn=\"");
    deleteOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (deleteOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * delete response associated with the provided delete operation.
   *
   * @param deleteOperation
   *          The delete operation containing the information to use
   *          to log the delete response.
   */
  public void logDeleteResponse(DeleteOperation deleteOperation)
  {
    if (!isLoggable(deleteOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(deleteOperation, "DELETE", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(deleteOperation.getResultCode().getIntValue());
    MessageBuilder msg = deleteOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = deleteOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    DN proxiedAuthDN = deleteOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append('\"');
    }
    buffer.append(" etime=");
    long etime = deleteOperation.getProcessingNanoTime();
    if (etime <= -1)
    {
      etime = deleteOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * termination of an existing client connection.
   *
   * @param  clientConnection  The client connection that has been terminated.
   * @param  disconnectReason  A generic disconnect reason for the connection
   *                           termination.
   * @param  message           A human-readable message that can provide
   *                           additional information about the disconnect.
   * @param clientConnection
   *          The client connection that has been terminated.
   * @param disconnectReason
   *          A generic disconnect reason for the connection
   *          termination.
   * @param message
   *          A human-readable message that can provide additional
   *          information about the disconnect.
   */
  public void logDisconnect(ClientConnection clientConnection,
                            DisconnectReason disconnectReason,
                            Message message)
      DisconnectReason disconnectReason, Message message)
  {
    long connectionID = clientConnection.getConnectionID();
    if (connectionID < 0 && suppressInternalOperations)
@@ -413,766 +968,94 @@
  /**
   * Writes a message to the access logger with information about the abandon
   * request associated with the provided abandon operation.
   *
   * @param  abandonOperation  The abandon operation containing the information
   *                           to use to log the abandon request.
   * {@inheritDoc}
   */
  public void logAbandonRequest(AbandonOperation abandonOperation)
  public void logExtendedIntermediateMessage(
      ExtendedOperation extendedOperation, String category,
      Map<String, String> content)
  {
    long connectionID = abandonOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (abandonOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" ABANDON conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(abandonOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(abandonOperation.getMessageID());
    buffer.append(" idToAbandon=");
    buffer.append(abandonOperation.getIDToAbandon());
    if (abandonOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the result
   * of the provided abandon operation.
   *
   * @param  abandonOperation  The abandon operation containing the information
   *                           to use to log the abandon request.
   */
  public void logAbandonResult(AbandonOperation abandonOperation)
  {
    long connectionID = abandonOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (abandonOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" ABANDON conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(abandonOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(abandonOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(abandonOperation.getResultCode());
    buffer.append("\"");
    MessageBuilder msg = abandonOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append("\"");
    }
    msg = abandonOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append("\"");
    }
    buffer.append(" etime=");
    buffer.append(abandonOperation.getProcessingTime());
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the add
   * request associated with the provided add operation.
   *
   * @param  addOperation  The add operation containing the information to use
   *                       to log the add request.
   */
  public void logAddRequest(AddOperation addOperation)
  {
    long connectionID = addOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (addOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" ADD conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(addOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(addOperation.getMessageID());
    buffer.append(" dn=\"");
    addOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (addOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the add
   * response associated with the provided add operation.
   *
   * @param  addOperation  The add operation containing the information to use
   *                       to log the add response.
   */
  public void logAddResponse(AddOperation addOperation)
  {
    long connectionID = addOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (addOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" ADD conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(addOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(addOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(addOperation.getResultCode());
    MessageBuilder msg = addOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(msg);
    }
    msg = addOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(msg);
    }
    DN proxiedAuthDN = addOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append("\" authzDN=\"");
      proxiedAuthDN.toString(buffer);
    }
    buffer.append("\" etime=");
    buffer.append(addOperation.getProcessingTime());
    writer.writeRecord(buffer.toString());
    logIntermediateMessage(extendedOperation, "EXTENDED", category, content);
  }
  /**
   * Writes a message to the access logger with information about the bind
   * request associated with the provided bind operation.
   * Writes a message to the access logger with information about the
   * extended request associated with the provided extended operation.
   *
   * @param  bindOperation  The bind operation with the information to use
   *                        to log the bind request.
   */
  public void logBindRequest(BindOperation bindOperation)
  {
    long connectionID = bindOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (bindOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" BIND conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(bindOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(bindOperation.getMessageID());
    switch (bindOperation.getAuthenticationType())
    {
      case SIMPLE:
        buffer.append(" type=SIMPLE");
        break;
      case SASL:
        buffer.append(" type=SASL mechanism=");
        buffer.append(bindOperation.getSASLMechanism());
        break;
      default:
        buffer.append(" type=");
        buffer.append(bindOperation.getAuthenticationType());
        break;
    }
    buffer.append(" dn=\"");
    bindOperation.getRawBindDN().toString(buffer);
    buffer.append("\"");
    if (bindOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the bind
   * response associated with the provided bind operation.
   *
   * @param  bindOperation  The bind operation containing the information to use
   *                        to log the bind response.
   */
  public void logBindResponse(BindOperation bindOperation)
  {
    long connectionID = bindOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (bindOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" BIND conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(bindOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(bindOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(bindOperation.getResultCode());
    MessageBuilder msg = bindOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(msg);
    }
    Message failureMessage = bindOperation.getAuthFailureReason();
    if (failureMessage != null)
    {
      buffer.append("\" authFailureID=");
      buffer.append(failureMessage.getDescriptor().getId());
      buffer.append(" authFailureReason=\"");
      buffer.append(failureMessage);
    }
    msg = bindOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(msg);
    }
    if (bindOperation.getResultCode() == ResultCode.SUCCESS)
    {
      AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo();
      if (authInfo != null)
      {
        DN authDN = authInfo.getAuthenticationDN();
        buffer.append("\" authDN=\"");
        if (authDN != null)
        {
          authDN.toString(buffer);
          DN authzDN = authInfo.getAuthorizationDN();
          if (! authDN.equals(authzDN))
          {
            buffer.append("\" authzDN=\"");
            if (authzDN != null)
            {
              authzDN.toString(buffer);
            }
          }
        }
      }
    }
    buffer.append("\" etime=");
    long etime = bindOperation.getProcessingNanoTime();
    if(etime <= -1)
    {
      etime = bindOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the compare
   * request associated with the provided compare operation.
   *
   * @param  compareOperation  The compare operation containing the information
   *                           to use to log the compare request.
   */
  public void logCompareRequest(CompareOperation compareOperation)
  {
    long connectionID = compareOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (compareOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" COMPARE conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(compareOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(compareOperation.getMessageID());
    buffer.append(" dn=\"");
    compareOperation.getRawEntryDN().toString(buffer);
    buffer.append("\" attr=");
    buffer.append(compareOperation.getAttributeType());
    if (compareOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the compare
   * response associated with the provided compare operation.
   *
   * @param  compareOperation  The compare operation containing the information
   *                           to use to log the compare response.
   */
  public void logCompareResponse(CompareOperation compareOperation)
  {
    long connectionID = compareOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (compareOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" COMPARE conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(compareOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(compareOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(compareOperation.getResultCode());
    MessageBuilder msg = compareOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(msg);
    }
    msg = compareOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(msg);
    }
    DN proxiedAuthDN = compareOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append("\" authzDN=\"");
      proxiedAuthDN.toString(buffer);
    }
    buffer.append("\" etime=");
    long etime = compareOperation.getProcessingNanoTime();
    if(etime <= -1)
    {
      etime = compareOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the delete
   * request associated with the provided delete operation.
   *
   * @param  deleteOperation  The delete operation with the information to
   *                          use to log the delete request.
   */
  public void logDeleteRequest(DeleteOperation deleteOperation)
  {
    long connectionID = deleteOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (deleteOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" DELETE conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(deleteOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(deleteOperation.getMessageID());
    buffer.append(" dn=\"");
    deleteOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (deleteOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the delete
   * response associated with the provided delete operation.
   *
   * @param  deleteOperation The delete operation containing the information to
   *                           use to log the delete response.
   */
  public void logDeleteResponse(DeleteOperation deleteOperation)
  {
    long connectionID = deleteOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (deleteOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" DELETE conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(deleteOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(deleteOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(deleteOperation.getResultCode());
    MessageBuilder msg = deleteOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(msg);
    }
    msg = deleteOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(msg);
    }
    DN proxiedAuthDN = deleteOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append("\" authzDN=\"");
      proxiedAuthDN.toString(buffer);
    }
    buffer.append("\" etime=");
    long etime = deleteOperation.getProcessingNanoTime();
    if(etime <= -1)
    {
      etime = deleteOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the extended
   * request associated with the provided extended operation.
   *
   * @param  extendedOperation  The extended operation containing the
   *                            information to use to log the extended request.
   * @param extendedOperation
   *          The extended operation containing the information to use
   *          to log the extended request.
   */
  public void logExtendedRequest(ExtendedOperation extendedOperation)
  {
    long connectionID = extendedOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(extendedOperation))
    {
      // This is an internal operation.
      if (extendedOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" EXTENDED conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(extendedOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(extendedOperation.getMessageID());
    appendHeader(extendedOperation, "EXTENDED", CATEGORY_REQUEST, buffer);
    buffer.append(" oid=\"");
    buffer.append(extendedOperation.getRequestOID());
    buffer.append("\"");
    if (extendedOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the extended
   * response associated with the provided extended operation.
   * Writes a message to the access logger with information about the
   * extended response associated with the provided extended
   * operation.
   *
   * @param  extendedOperation  The extended operation containing the
   *                            info to use to log the extended response.
   * @param extendedOperation
   *          The extended operation containing the info to use to log
   *          the extended response.
   */
  public void logExtendedResponse(ExtendedOperation extendedOperation)
  {
    long connectionID = extendedOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(extendedOperation))
    {
      // This is an internal operation.
      if (extendedOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" EXTENDED conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(extendedOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(extendedOperation.getMessageID());
    appendHeader(extendedOperation, "EXTENDED", CATEGORY_RESPONSE, buffer);
    String oid = extendedOperation.getResponseOID();
    if (oid != null)
    {
      buffer.append(" oid=\"");
      buffer.append(oid);
      buffer.append("\"");
      buffer.append('\"');
    }
    buffer.append(" result=\"");
    buffer.append(extendedOperation.getResultCode());
    buffer.append(" result=");
    buffer.append(extendedOperation.getResultCode().getIntValue());
    MessageBuilder msg = extendedOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = extendedOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    buffer.append("\" etime=");
    buffer.append(" etime=");
    long etime = extendedOperation.getProcessingNanoTime();
    if(etime <= -1)
    if (etime <= -1)
    {
      etime = extendedOperation.getProcessingTime();
    }
@@ -1184,168 +1067,35 @@
  /**
   * Writes a message to the access logger with information about the modify
   * request associated with the provided modify operation.
   *
   * @param  modifyOperation The modify operation containing the information to
   *                         use to log the modify request.
   * {@inheritDoc}
   */
  public void logModifyRequest(ModifyOperation modifyOperation)
  public void logModifyDNIntermediateMessage(
      ModifyDNOperation modifyDNOperation, String category,
      Map<String, String> content)
  {
    long connectionID = modifyOperation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (modifyOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" MODIFY conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(modifyOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(modifyOperation.getMessageID());
    buffer.append(" dn=\"");
    modifyOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (modifyOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
    logIntermediateMessage(modifyDNOperation, "MODIFY", category, content);
  }
  /**
   * Writes a message to the access logger with information about the modify
   * response associated with the provided modify operation.
   * Writes a message to the access logger with information about the
   * modify DN request associated with the provided modify DN
   * operation.
   *
   * @param  modifyOperation The modify operation containing the information to
   *                         use to log the modify response.
   */
  public void logModifyResponse(ModifyOperation modifyOperation)
  {
    long connectionID = modifyOperation.getConnectionID();
    if (connectionID < 0)
    {
      // this is an internal operation
      if (modifyOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" MODIFY conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(modifyOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(modifyOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(modifyOperation.getResultCode());
    MessageBuilder msg = modifyOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(msg);
    }
    msg = modifyOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(msg);
    }
    DN proxiedAuthDN = modifyOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append("\" authzDN=\"");
      proxiedAuthDN.toString(buffer);
    }
    buffer.append("\" etime=");
    long etime = modifyOperation.getProcessingNanoTime();
    if(etime <= -1)
    {
      etime = modifyOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the modify DN
   * request associated with the provided modify DN operation.
   *
   * @param  modifyDNOperation  The modify DN operation containing the
   *                            info to use to log the modify DN request.
   * @param modifyDNOperation
   *          The modify DN operation containing the info to use to
   *          log the modify DN request.
   */
  public void logModifyDNRequest(ModifyDNOperation modifyDNOperation)
  {
    long connectionID = modifyDNOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(modifyDNOperation))
    {
      // This is an internal operation.
      if (modifyDNOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" MODIFYDN conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(modifyDNOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(modifyDNOperation.getMessageID());
    appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_REQUEST, buffer);
    buffer.append(" dn=\"");
    modifyDNOperation.getRawEntryDN().toString(buffer);
    buffer.append("\" newRDN=\"");
@@ -1362,78 +1112,59 @@
    if (modifyDNOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the modify DN
   * response associated with the provided modify DN operation.
   * Writes a message to the access logger with information about the
   * modify DN response associated with the provided modify DN
   * operation.
   *
   * @param  modifyDNOperation  The modify DN operation containing the
   *                            information to use to log the modify DN
   *                            response.
   * @param modifyDNOperation
   *          The modify DN operation containing the information to
   *          use to log the modify DN response.
   */
  public void logModifyDNResponse(ModifyDNOperation modifyDNOperation)
  {
    long connectionID = modifyDNOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(modifyDNOperation))
    {
      // This is an internal operation.
      if (modifyDNOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" MODIFYDN conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(modifyDNOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(modifyDNOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(modifyDNOperation.getResultCode());
    appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(modifyDNOperation.getResultCode().getIntValue());
    MessageBuilder msg = modifyDNOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = modifyDNOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" additionalInfo=\"");
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    DN proxiedAuthDN = modifyDNOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append("\" authzDN=\"");
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append('\"');
    }
    buffer.append("\" etime=");
    buffer.append(" etime=");
    long etime = modifyDNOperation.getProcessingNanoTime();
    if(etime <= -1)
    if (etime <= -1)
    {
      etime = modifyDNOperation.getProcessingTime();
    }
@@ -1443,44 +1174,131 @@
  }
  /**
   * Writes a message to the access logger with information about the search
   * request associated with the provided search operation.
   * {@inheritDoc}
   */
  public void logModifyIntermediateMessage(ModifyOperation modifyOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(modifyOperation, "MODIFY", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * modify request associated with the provided modify operation.
   *
   * @param  searchOperation  The search operation containing the info to
   *                          use to log the search request.
   * @param modifyOperation
   *          The modify operation containing the information to use
   *          to log the modify request.
   */
  public void logModifyRequest(ModifyOperation modifyOperation)
  {
    if (!isLoggable(modifyOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(modifyOperation, "MODIFY", CATEGORY_REQUEST, buffer);
    buffer.append(" dn=\"");
    modifyOperation.getRawEntryDN().toString(buffer);
    buffer.append("\"");
    if (modifyOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the
   * modify response associated with the provided modify operation.
   *
   * @param modifyOperation
   *          The modify operation containing the information to use
   *          to log the modify response.
   */
  public void logModifyResponse(ModifyOperation modifyOperation)
  {
    if (!isLoggable(modifyOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(modifyOperation, "MODIFY", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(modifyOperation.getResultCode().getIntValue());
    MessageBuilder msg = modifyOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    msg = modifyOperation.getAdditionalLogMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    DN proxiedAuthDN = modifyOperation.getProxiedAuthorizationDN();
    if (proxiedAuthDN != null)
    {
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append('\"');
    }
    buffer.append(" etime=");
    long etime = modifyOperation.getProcessingNanoTime();
    if (etime <= -1)
    {
      etime = modifyOperation.getProcessingTime();
    }
    buffer.append(etime);
    writer.writeRecord(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  public void logSearchIntermediateMessage(SearchOperation searchOperation,
      String category, Map<String, String> content)
  {
    logIntermediateMessage(searchOperation, "SEARCH", category, content);
  }
  /**
   * Writes a message to the access logger with information about the
   * search request associated with the provided search operation.
   *
   * @param searchOperation
   *          The search operation containing the info to use to log
   *          the search request.
   */
  public void logSearchRequest(SearchOperation searchOperation)
  {
    long connectionID = searchOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(searchOperation))
    {
      // This is an internal operation.
      if (searchOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" SEARCH conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(searchOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(searchOperation.getMessageID());
    appendHeader(searchOperation, "SEARCH", CATEGORY_REQUEST, buffer);
    buffer.append(" base=\"");
    searchOperation.getRawBaseDN().toString(buffer);
    buffer.append("\" scope=");
@@ -1510,93 +1328,40 @@
    if (searchOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  /**
   * Writes a message to the access logger with information about the search
   * result entry that matches the criteria associated with the provided search
   * operation.
   *
   * @param  searchOperation  The search operation with which the search result
   *                          entry is associated.
   * @param  searchEntry      The search result entry to be logged.
   */
  public void logSearchResultEntry(SearchOperation searchOperation,
                                     SearchResultEntry searchEntry)
  {
    // NYI
  }
  /**
   * Writes a message to the access logger with information about the search
   * result reference returned while processing the associated search
   * operation.
   *
   * @param  searchOperation  The search operation with which the search result
   *                          reference is associated.
   * @param  searchReference  The search result reference to be logged.
   */
  public void logSearchResultReference(SearchOperation searchOperation,
                            SearchResultReference searchReference)
  {
    // NYI
  }
  /**
   * Writes a message to the access logger with information about the
   * completion of the provided search operation.
   *
   * @param  searchOperation  The search operation containing the information
   *                          to use to log the search result done message.
   * @param searchOperation
   *          The search operation containing the information to use
   *          to log the search result done message.
   */
  public void logSearchResultDone(SearchOperation searchOperation)
  {
    long connectionID = searchOperation.getConnectionID();
    if (connectionID < 0)
    if (!isLoggable(searchOperation))
    {
      // This is an internal operation.
      if (searchOperation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
        }
      }
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" SEARCH conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(searchOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(searchOperation.getMessageID());
    buffer.append(" result=\"");
    buffer.append(searchOperation.getResultCode());
    appendHeader(searchOperation, "SEARCH", CATEGORY_RESPONSE, buffer);
    buffer.append(" result=");
    buffer.append(searchOperation.getResultCode().getIntValue());
    MessageBuilder msg = searchOperation.getErrorMessage();
    if ((msg != null) && (msg.length() > 0))
    {
      buffer.append("\" message=\"");
      buffer.append(" message=\"");
      buffer.append(msg);
      buffer.append('\"');
    }
    buffer.append("\" nentries=");
    buffer.append(" nentries=");
    buffer.append(searchOperation.getEntriesSent());
    msg = searchOperation.getAdditionalLogMessage();
@@ -1604,7 +1369,7 @@
    {
      buffer.append(" additionalInfo=\"");
      buffer.append(msg);
      buffer.append("\"");
      buffer.append('\"');
    }
    DN proxiedAuthDN = searchOperation.getProxiedAuthorizationDN();
@@ -1612,12 +1377,12 @@
    {
      buffer.append(" authzDN=\"");
      proxiedAuthDN.toString(buffer);
      buffer.append("\"");
      buffer.append('\"');
    }
    buffer.append(" etime=");
    long etime = searchOperation.getProcessingNanoTime();
    if(etime <= -1)
    if (etime <= -1)
    {
      etime = searchOperation.getProcessingTime();
    }
@@ -1629,63 +1394,97 @@
  /**
   * Writes a message to the access logger with information about the unbind
   * request associated with the provided unbind operation.
   * Writes a message to the access logger with information about the
   * unbind request associated with the provided unbind operation.
   *
   * @param  unbindOperation  The unbind operation containing the info to
   *                          use to log the unbind request.
   * @param unbindOperation
   *          The unbind operation containing the info to use to log
   *          the unbind request.
   */
  public void logUnbind(UnbindOperation unbindOperation)
  {
    long connectionID = unbindOperation.getConnectionID();
    if (!isLoggable(unbindOperation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(unbindOperation, "UNBIND", CATEGORY_REQUEST, buffer);
    if (unbindOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    writer.writeRecord(buffer.toString());
  }
  // Appends the common log header information to the provided buffer.
  private void appendHeader(Operation operation, String opType,
      String category, StringBuilder buffer)
  {
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("] ");
    buffer.append(opType);
    buffer.append(" ");
    buffer.append(category);
    buffer.append(" conn=");
    buffer.append(operation.getConnectionID());
    buffer.append(" op=");
    buffer.append(operation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(operation.getMessageID());
  }
  // Determines whether the provided operation should be logged.
  private boolean isLoggable(Operation operation)
  {
    long connectionID = operation.getConnectionID();
    if (connectionID < 0)
    {
      // This is an internal operation.
      if (unbindOperation.isSynchronizationOperation())
      if (operation.isSynchronizationOperation())
      {
        if (suppressSynchronizationOperations)
        {
          return;
          return false;
        }
      }
      else
      {
        if (suppressInternalOperations)
        {
          return;
          return false;
        }
      }
    }
    StringBuilder buffer = new StringBuilder(50);
    buffer.append("[");
    buffer.append(TimeThread.getLocalTime());
    buffer.append("]");
    buffer.append(" UNBIND conn=");
    buffer.append(connectionID);
    buffer.append(" op=");
    buffer.append(unbindOperation.getOperationID());
    buffer.append(" msgID=");
    buffer.append(unbindOperation.getMessageID());
    if (unbindOperation.isSynchronizationOperation())
      buffer.append(" type=synchronization");
    return true;
  }
  // Writes an intermediate message to the log.
  private void logIntermediateMessage(Operation operation, String opType,
      String category, Map<String, String> content)
  {
    if (!isLoggable(operation))
    {
      return;
    }
    StringBuilder buffer = new StringBuilder(50);
    appendHeader(operation, opType, category, buffer);
    for (Map.Entry<String, String> entry : content.entrySet())
    {
      buffer.append(' ');
      buffer.append(entry.getKey());
      buffer.append('=');
      buffer.append(entry.getValue());
    }
    writer.writeRecord(buffer.toString());
  }
  /**
   * {@inheritDoc}
   */
  public DN getDN()
  {
    if(currentConfig != null)
    {
      return currentConfig.dn();
    }
    else
    {
      return null;
    }
  }
}