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

Jean-Noel Rouvignac
25.00.2013 b54d8bdf7b583b5d57b7deae2431c32fcd644a1a
OPENDJ-879 (CR-1603) Add HTTP access log


Consolidated common code for the *Logger classes prior to implementing the HTTP access log.


AbstractLogger.java: ADDED
Pulled up from the *Logger classes.

AccessLogger.java, ErrorLogger.java, DebugLogger.java:
Now inherit from AbstractLogger.
Removed code pulled up into AbstractLogger: initializeErrorLogger(), isConfigurationAddAcceptable(), applyConfigurationAdd(), applyConfigurationChange(), isConfigurationDeleteAcceptable(), applyConfigurationDelete(), isJavaClassAcceptable(), getErrorPublisher().
Added the loggerStorage static variable.
Added a ctor + implemented getJavaClassPropertyDefinition() and getStorage().

LoggerConfigManager.java:
Replaced the calls to the various initialize*Logger() methods with calling initializeLogger().
Extracted method getLoggerInstance() and used it in all the other methods.


LogPublisherConfiguration.xml:
Pulled up the declaration of "java-class" property here from sub configurations.

AccessLogPublisherConfiguration.xml, DebugLogPublisherConfiguration.xml, ErrorLogPublisherConfiguration.xml, LogPublisherConfiguration.xml:
Replaced the declaration of "java-class" property by a property-override.

StaticUtils.java:
Added close(Collection<Closeable>)
1 files added
10 files modified
1764 ■■■■■ changed files
opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml 28 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/DebugLogPublisherConfiguration.xml 27 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/ErrorLogPublisherConfiguration.xml 27 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/LogPublisherConfiguration.xml 20 ●●●●● patch | view | raw | blame | history
opends/src/admin/messages/LogPublisherCfgDefn.properties 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/LoggerConfigManager.java 163 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/AbstractLogger.java 409 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/AccessLogger.java 381 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/ErrorLogger.java 339 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/debug/DebugLogger.java 349 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 20 ●●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/AccessLogPublisherConfiguration.xml
@@ -24,7 +24,7 @@
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Portions copyright 2011 ForgeRock AS.
  !      Portions copyright 2011-2013 ForgeRock AS
  ! -->
<adm:managed-object name="access-log-publisher"
  plural-name="access-log-publishers"
@@ -59,25 +59,13 @@
      <ldap:rdn-sequence>cn=Filtering Criteria</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:property name="java-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.AccessLogPublisher
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-java-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-override name="java-class">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>org.opends.server.api.AccessLogPublisher</adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="filtering-policy">
    <adm:synopsis>
      Specifies how filtering criteria should be applied to log records. 
opends/src/admin/defn/org/opends/server/admin/std/DebugLogPublisherConfiguration.xml
@@ -24,6 +24,7 @@
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Portions copyright 2013 ForgeRock AS
  ! -->
<adm:managed-object name="debug-log-publisher"
  plural-name="debug-log-publishers"
@@ -62,25 +63,13 @@
      </cli:relation>
    </adm:profile>
  </adm:relation>
  <adm:property name="java-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.DebugLogPublisher
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-java-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-override name="java-class">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>org.opends.server.api.DebugLogPublisher</adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="default-debug-level" mandatory="true">
    <adm:synopsis>
      The lowest severity level of debug messages to log when none of
opends/src/admin/defn/org/opends/server/admin/std/ErrorLogPublisherConfiguration.xml
@@ -24,6 +24,7 @@
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Portions copyright 2013 ForgeRock AS
  ! -->
<adm:managed-object name="error-log-publisher"
  plural-name="error-log-publishers"
@@ -49,25 +50,13 @@
  <adm:profile name="cli">
    <cli:managed-object custom="true" />
  </adm:profile>
  <adm:property name="java-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.ErrorLogPublisher
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-java-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-override name="java-class">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>org.opends.server.api.ErrorLogPublisher</adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="default-severity" multi-valued="true">
    <adm:synopsis>
      Specifies the default severity levels for the logger.
opends/src/admin/defn/org/opends/server/admin/std/LogPublisherConfiguration.xml
@@ -24,6 +24,7 @@
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Portions copyright 2013 ForgeRock AS
  ! -->
<adm:managed-object name="log-publisher" plural-name="log-publishers"
  package="org.opends.server.admin.std" abstract="true"
@@ -56,4 +57,23 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="java-class" mandatory="true">
    <adm:synopsis>
      The fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.LogPublisher
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-java-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/messages/LogPublisherCfgDefn.properties
@@ -2,3 +2,4 @@
user-friendly-plural-name=Log Publishers
synopsis=Log Publishers are responsible for distributing log messages from different loggers to a destination.
property.enabled.synopsis=Indicates whether the Log Publisher is enabled for use.
property.java-class.synopsis=The fully-qualified name of the Java class that provides the Log Publisher implementation.
opends/src/server/org/opends/server/core/LoggerConfigManager.java
@@ -23,27 +23,29 @@
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS
 */
package org.opends.server.core;
import org.opends.messages.Message;
import java.util.*;
import org.opends.server.config.ConfigException;
import org.opends.server.types.*;
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.loggers.AccessLogger;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.loggers.ErrorLogger.*;
import org.opends.server.admin.std.server.*;
import java.util.ArrayList;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.server.*;
import org.opends.server.config.ConfigException;
import org.opends.server.loggers.AbstractLogger;
import org.opends.server.loggers.AccessLogger;
import org.opends.server.loggers.ErrorLogger;
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
/**
@@ -123,67 +125,77 @@
      logError(WARN_CONFIG_LOGGER_NO_ACTIVE_ERROR_LOGGERS.get());
    }
    DebugLogger.getInstance().initializeDebugLogger(debugPublisherCfgs);
    AccessLogger.getInstance().initializeAccessLogger(accessPublisherCfgs);
    ErrorLogger.getInstance().initializeErrorLogger(errorPublisherCfgs);
    DebugLogger.getInstance().initializeLogger(debugPublisherCfgs);
    AccessLogger.getInstance().initializeLogger(accessPublisherCfgs);
    ErrorLogger.getInstance().initializeLogger(errorPublisherCfgs);
  }
  /**
   * Returns the logger instance corresponding to the provided config. If no
   * logger corresponds to it, null will be returned and a message will be added
   * to the provided messages list.
   *
   * @param config
   *          the config for which to return the logger instance
   * @param messages
   *          where the error message will be output if no logger correspond to
   *          the provided config.
   * @return the logger corresponding to the provided config, null if no logger
   *         corresponds.
   */
  private AbstractLogger getLoggerInstance(LogPublisherCfg config,
      List<Message> messages)
  {
    if (config instanceof DebugLogPublisherCfg)
    {
      return DebugLogger.getInstance();
    }
    else if (config instanceof AccessLogPublisherCfg)
    {
      return AccessLogger.getInstance();
    }
    else if (config instanceof ErrorLogPublisherCfg)
    {
      return ErrorLogger.getInstance();
    }
    else
    {
      messages.add(ERR_CONFIG_LOGGER_INVALID_OBJECTCLASS.get(String
          .valueOf(config.dn())));
      return null;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAddAcceptable(LogPublisherCfg config,
                                              List<Message> unacceptableReasons)
  {
    if(config instanceof DebugLogPublisherCfg)
    AbstractLogger instance = getLoggerInstance(config, unacceptableReasons);
    if (instance != null)
    {
      return DebugLogger.getInstance().isConfigurationAddAcceptable(
          (DebugLogPublisherCfg)config, unacceptableReasons);
      return instance.isConfigurationAddAcceptable(config, unacceptableReasons);
    }
   else if(config instanceof AccessLogPublisherCfg)
   {
     return AccessLogger.getInstance().isConfigurationAddAcceptable(
         (AccessLogPublisherCfg)config, unacceptableReasons);
   }
   else if(config instanceof ErrorLogPublisherCfg)
   {
     return ErrorLogger.getInstance().isConfigurationAddAcceptable(
         (ErrorLogPublisherCfg)config, unacceptableReasons);
   }
    else
    {
      unacceptableReasons.add(ERR_CONFIG_LOGGER_INVALID_OBJECTCLASS.get(
              String.valueOf(config.dn())));
      return false;
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationAdd(LogPublisherCfg config)
  {
    if(config instanceof DebugLogPublisherCfg)
    List<Message> messages = new ArrayList<Message>(1);
    AbstractLogger instance = getLoggerInstance(config, messages);
    if (instance != null)
    {
      return DebugLogger.getInstance().applyConfigurationAdd(
          (DebugLogPublisherCfg)config);
      return instance.applyConfigurationAdd(config);
    }
   else if(config instanceof AccessLogPublisherCfg)
   {
     return AccessLogger.getInstance().applyConfigurationAdd(
         (AccessLogPublisherCfg)config);
   }
   else if(config instanceof ErrorLogPublisherCfg)
   {
     return ErrorLogger.getInstance().applyConfigurationAdd(
         (ErrorLogPublisherCfg)config);
   }
    else
    {
      ArrayList<Message> messages            = new ArrayList<Message>();
      messages.add(ERR_CONFIG_LOGGER_INVALID_OBJECTCLASS.
              get(String.valueOf(config.dn())));
      boolean           adminActionRequired = false;
      boolean adminActionRequired = false;
      ResultCode resultCode = ResultCode.UNWILLING_TO_PERFORM;
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
@@ -192,61 +204,36 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationDeleteAcceptable(LogPublisherCfg config,
                                              List<Message> unacceptableReasons)
  {
    if(config instanceof DebugLogPublisherCfg)
    AbstractLogger instance = getLoggerInstance(config, unacceptableReasons);
    if (instance != null)
    {
      return DebugLogger.getInstance().isConfigurationDeleteAcceptable(
          (DebugLogPublisherCfg)config, unacceptableReasons);
      return instance.isConfigurationDeleteAcceptable(config,
          unacceptableReasons);
    }
   else if(config instanceof AccessLogPublisherCfg)
   {
     return AccessLogger.getInstance().isConfigurationDeleteAcceptable(
         (AccessLogPublisherCfg)config, unacceptableReasons);
   }
   else if(config instanceof ErrorLogPublisherCfg)
   {
     return ErrorLogger.getInstance().isConfigurationDeleteAcceptable(
         (ErrorLogPublisherCfg)config, unacceptableReasons);
   }
    else
    {
      unacceptableReasons.add(ERR_CONFIG_LOGGER_INVALID_OBJECTCLASS.get(
              String.valueOf(config.dn())));
      return false;
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationDelete(LogPublisherCfg config)
  {
    if(config instanceof DebugLogPublisherCfg)
    List<Message> messages = new ArrayList<Message>(1);
    AbstractLogger instance = getLoggerInstance(config, messages);
    if (instance != null)
    {
      return DebugLogger.getInstance().applyConfigurationDelete(
          (DebugLogPublisherCfg)config);
      return instance.applyConfigurationDelete(config);
    }
   else if(config instanceof AccessLogPublisherCfg)
   {
     return AccessLogger.getInstance().applyConfigurationDelete(
         (AccessLogPublisherCfg)config);
   }
   else if(config instanceof ErrorLogPublisherCfg)
   {
     return ErrorLogger.getInstance().applyConfigurationDelete(
         (ErrorLogPublisherCfg)config);
   }
    else
    {
      ArrayList<Message> messages            = new ArrayList<Message>();
      messages.add(ERR_CONFIG_LOGGER_INVALID_OBJECTCLASS.get(
              String.valueOf(config.dn())));
      boolean           adminActionRequired = false;
      ResultCode resultCode = ResultCode.UNWILLING_TO_PERFORM;
      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
    }
  }
}
opends/src/server/org/opends/server/loggers/AbstractLogger.java
New file
@@ -0,0 +1,409 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Portions copyright 2011-2013 ForgeRock AS.
 */
package org.opends.server.loggers;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.opends.messages.Message;
import org.opends.messages.MessageDescriptor.Arg3;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.server.LogPublisherCfg;
import org.opends.server.api.LogPublisher;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugLogger;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
/**
 * This class defines the wrapper that will invoke all registered loggers for
 * each type of request received or response sent. If no log publishers are
 * registered, messages will be directed to standard out.
 *
 * @param <P>
 *          The type of the LogPublisher corresponding to this logger
 * @param <C>
 *          The type of the LogPublisherCfg corresponding to this logger
 */
public abstract class AbstractLogger
    <P extends LogPublisher<C>, C extends LogPublisherCfg>
    implements ConfigurationAddListener<C>, ConfigurationDeleteListener<C>,
    ConfigurationChangeListener<C>
{
  /**
   * The storage designed to store log publishers. It is helpful in abstracting
   * away the methods used to manage the collection.
   *
   * @param <P>
   *          The concrete {@link LogPublisher} type
   * @param <C>
   *          The concrete {@link LogPublisherCfg} type
   */
  protected static class LoggerStorage<P extends LogPublisher<C>,
      C extends LogPublisherCfg>
  {
    /**
     * Defined as public to allow subclasses of {@link AbstractLogger} to
     * instantiate it.
     */
    public LoggerStorage()
    {
      super();
    }
    /**
     * The set of loggers that have been registered with the server. It will
     * initially be empty.
     */
    private Collection<P> logPublishers = new CopyOnWriteArrayList<P>();
    /**
     * Add a log publisher to the logger.
     *
     * @param publisher
     *          The log publisher to add.
     */
    public synchronized void addLogPublisher(P publisher)
    {
      logPublishers.add(publisher);
    }
    /**
     * Remove a log publisher from the logger.
     *
     * @param publisher
     *          The log publisher to remove.
     * @return True if the log publisher is removed or false otherwise.
     */
    public synchronized boolean removeLogPublisher(P publisher)
    {
      boolean removed = logPublishers.remove(publisher);
      if (removed)
      {
        publisher.close();
      }
      return removed;
    }
    /**
     * Removes all existing log publishers from the logger.
     */
    public synchronized void removeAllLogPublishers()
    {
      StaticUtils.close((Collection) logPublishers);
      logPublishers.clear();
    }
    /**
     * Returns the logPublishers.
     *
     * @return the collection of {@link LogPublisher}s
     */
    public Collection<P> getLogPublishers()
    {
      return logPublishers;
    }
  }
  /**
   * Returns the logger storage for the current logger.
   *
   * @return the logger storage for the current logger
   */
  protected abstract LoggerStorage<P, C> getStorage();
  /**
   * Returns the java {@link ClassPropertyDefinition} for the current logger.
   *
   * @return the java {@link ClassPropertyDefinition} for the current logger.
   */
  protected abstract ClassPropertyDefinition getJavaClassPropertyDefinition();
  private final Class<P> logPublisherClass;
  private final Arg3<CharSequence, CharSequence, CharSequence>
      invalidLoggerClassErrorMessage;
  /**
   * The constructor for this class.
   *
   * @param logPublisherClass
   *          the log publisher class
   * @param invalidLoggerClassErrorMessage
   *          the error message to use if the logger class in invalid
   */
  public AbstractLogger(
      final Class<P> logPublisherClass,
      final Arg3<CharSequence, CharSequence, CharSequence>
          invalidLoggerClassErrorMessage)
  {
    this.logPublisherClass = logPublisherClass;
    this.invalidLoggerClassErrorMessage = invalidLoggerClassErrorMessage;
  }
  /**
   * Initializes all the log publishers.
   *
   * @param configs The log publisher configurations.
   * @throws ConfigException
   *           If an unrecoverable problem arises in the process of
   *           performing the initialization as a result of the server
   *           configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization that is not
   *           related to the server configuration.
   */
  public void initializeLogger(List<C> configs)
      throws ConfigException, InitializationException
  {
    for (C config : configs)
    {
      config.addChangeListener((ConfigurationChangeListener) this);
      if(config.isEnabled())
      {
        getStorage().addLogPublisher(getLogPublisher(config));
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAddAcceptable(C config,
      List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationChangeAcceptable(C config,
      List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationAdd(C config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    config.addChangeListener((ConfigurationChangeListener) this);
    if(config.isEnabled())
    {
      try
      {
        getStorage().addLogPublisher(getLogPublisher(config));
      }
      catch(ConfigException e)
      {
        debugCaught(DebugLogLevel.ERROR, e);
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (Exception e)
      {
        debugCaught(DebugLogLevel.ERROR, e);
        messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
                String.valueOf(config.dn().toString()),
                stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  private void debugCaught(LogLevel error, Exception e)
  {
    if (DebugLogger.debugEnabled())
    {
      DebugLogger.getTracer().debugCaught(DebugLogLevel.ERROR, e);
    }
  }
  private P findLogPublisher(DN dn)
  {
    Collection<P> logPublishers = getStorage().getLogPublishers();
    for (P publisher : logPublishers)
    {
      if (publisher.getDN().equals(dn))
      {
        return publisher;
      }
    }
    return null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationChange(C config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    P logPublisher = findLogPublisher(config.dn());
    if(logPublisher == null)
    {
      if(config.isEnabled())
      {
        // Needs to be added and enabled.
        return applyConfigurationAdd(config);
      }
    }
    else
    {
      if(config.isEnabled())
      {
        // The publisher is currently active, so we don't need to do anything.
        // Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = config.getJavaClass();
        if(!className.equals(logPublisher.getClass().getName()))
        {
          adminActionRequired = true;
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        getStorage().removeLogPublisher(logPublisher);
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationDeleteAcceptable(C config,
      List<Message> unacceptableReasons)
  {
    return findLogPublisher(config.dn()) != null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationDelete(C config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    P logPublisher = findLogPublisher(config.dn());
    if(logPublisher != null)
    {
      getStorage().removeLogPublisher(logPublisher);
    }
    else
    {
      resultCode = ResultCode.NO_SUCH_OBJECT;
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  private boolean isJavaClassAcceptable(C config,
                                        List<Message> unacceptableReasons)
  {
    String className = config.getJavaClass();
    ClassPropertyDefinition pd = getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a LogPublisher.
      P publisher = pd.loadClass(className, logPublisherClass).newInstance();
      // The class is valid as far as we can tell.
      return publisher.isConfigurationAcceptable(config, unacceptableReasons);
    } catch (Exception e) {
      Message message =
          invalidLoggerClassErrorMessage.get(className, config.dn().toString(),
              String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
  }
  private P getLogPublisher(C config) throws ConfigException
  {
    String className = config.getJavaClass();
    ClassPropertyDefinition pd = getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a LogPublisher.
      P logPublisher = pd.loadClass(className, logPublisherClass).newInstance();
      logPublisher.initializeLogPublisher(config);
      // The log publisher has been successfully initialized.
      return logPublisher;
    }
    catch (Exception e)
    {
      Message message =
          invalidLoggerClassErrorMessage.get(className, config.dn().toString(),
              String.valueOf(e));
      throw new ConfigException(message, e);
    }
  }
}
opends/src/server/org/opends/server/loggers/AccessLogger.java
@@ -27,25 +27,16 @@
 */
package org.opends.server.loggers;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Collection;
import org.opends.messages.Message;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.meta.AccessLogPublisherCfgDefn;
import org.opends.server.admin.std.server.AccessLogPublisherCfg;
import org.opends.server.api.AccessLogPublisher;
import org.opends.server.api.ClientConnection;
import org.opends.server.config.ConfigException;
import org.opends.server.core.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.*;
@@ -53,25 +44,42 @@
 * This class defines the wrapper that will invoke all registered access loggers
 * for each type of request received or response sent.
 */
public class AccessLogger implements
    ConfigurationAddListener<AccessLogPublisherCfg>,
    ConfigurationDeleteListener<AccessLogPublisherCfg>,
    ConfigurationChangeListener<AccessLogPublisherCfg>
public class AccessLogger extends AbstractLogger
    <AccessLogPublisher<AccessLogPublisherCfg>, AccessLogPublisherCfg>
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // The set of access loggers that have been registered with the server.  It
  // will initially be empty.
  private static CopyOnWriteArrayList<AccessLogPublisher<?>> accessPublishers =
      new CopyOnWriteArrayList<AccessLogPublisher<?>>();
  private static LoggerStorage
      <AccessLogPublisher<AccessLogPublisherCfg>, AccessLogPublisherCfg>
      loggerStorage = new LoggerStorage
      <AccessLogPublisher<AccessLogPublisherCfg>, AccessLogPublisherCfg>();
  // The singleton instance of this class for configuration purposes.
  /** The singleton instance of this class for configuration purposes. */
  private static final AccessLogger instance = new AccessLogger();
  /**
   * The constructor for this class.
   */
  public AccessLogger()
  {
    super((Class) AccessLogPublisher.class,
        ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS);
  }
  /** {@inheritDoc} */
  @Override
  protected ClassPropertyDefinition getJavaClassPropertyDefinition()
  {
    return AccessLogPublisherCfgDefn.getInstance()
        .getJavaClassPropertyDefinition();
  }
  /** {@inheritDoc} */
  @Override
  protected LoggerStorage<AccessLogPublisher<AccessLogPublisherCfg>,
      AccessLogPublisherCfg> getStorage()
  {
    return loggerStorage;
  }
  /**
   * Retrieve the singleton instance of this class.
@@ -89,9 +97,9 @@
   * @param publisher The access log publisher to add.
   */
  public synchronized static void addAccessLogPublisher(
      AccessLogPublisher<?> publisher)
      AccessLogPublisher publisher)
  {
    accessPublishers.add(publisher);
    loggerStorage.addLogPublisher(publisher);
  }
  /**
@@ -101,16 +109,9 @@
   * @return The publisher that was removed or null if it was not found.
   */
  public synchronized static boolean removeAccessLogPublisher(
      AccessLogPublisher<?> publisher)
      AccessLogPublisher<AccessLogPublisherCfg> publisher)
  {
    boolean removed = accessPublishers.remove(publisher);
    if(removed)
    {
      publisher.close();
    }
    return removed;
    return loggerStorage.removeLogPublisher(publisher);
  }
  /**
@@ -118,274 +119,20 @@
   */
  public synchronized static void removeAllAccessLogPublishers()
  {
    for(AccessLogPublisher<?> publisher : accessPublishers)
    {
      publisher.close();
    }
    accessPublishers.clear();
    loggerStorage.removeAllLogPublishers();
  }
  /**
   * Initializes all the access log publishers.
   * Returns all the registered access log publishers.
   *
   * @param configs The access log publisher configurations.
   * @throws ConfigException
   *           If an unrecoverable problem arises in the process of
   *           performing the initialization as a result of the server
   *           configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization that is not
   *           related to the server configuration.
   * @return a Collection of {@link AccessLogPublisher} objects
   */
  public void initializeAccessLogger(List<AccessLogPublisherCfg> configs)
      throws ConfigException, InitializationException
  private static Collection
      <AccessLogPublisher<AccessLogPublisherCfg>> getAccessLogPublishers()
  {
    for(AccessLogPublisherCfg config : configs)
    {
      config.addAccessChangeListener(this);
      if(config.isEnabled())
      {
        AccessLogPublisher<?> AccessLogPublisher = getAccessPublisher(config);
        addAccessLogPublisher(AccessLogPublisher);
      }
    }
    return loggerStorage.getLogPublishers();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAddAcceptable(
      AccessLogPublisherCfg config,
      List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationChangeAcceptable(
      AccessLogPublisherCfg config,
      List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationAdd(AccessLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    config.addAccessChangeListener(this);
    if(config.isEnabled())
    {
      try
      {
        AccessLogPublisher<?> AccessLogPublisher = getAccessPublisher(config);
        addAccessLogPublisher(AccessLogPublisher);
      }
      catch(ConfigException e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
            String.valueOf(config.dn().toString()),
            stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationChange(
      AccessLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    DN dn = config.dn();
    AccessLogPublisher<?> accessLogPublisher = null;
    for(AccessLogPublisher<?> publisher : accessPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        accessLogPublisher = publisher;
        break;
      }
    }
    if(accessLogPublisher == null)
    {
      if(config.isEnabled())
      {
        // Needs to be added and enabled.
        return applyConfigurationAdd(config);
      }
    }
    else
    {
      if(config.isEnabled())
      {
        // The publisher is currently active, so we don't need to do anything.
        // Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = config.getJavaClass();
        if(!className.equals(accessLogPublisher.getClass().getName()))
        {
          adminActionRequired = true;
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        removeAccessLogPublisher(accessLogPublisher);
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationDeleteAcceptable(
      AccessLogPublisherCfg config,
      List<Message> unacceptableReasons)
  {
    DN dn = config.dn();
    AccessLogPublisher<?> accessLogPublisher = null;
    for(AccessLogPublisher<?> publisher : accessPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        accessLogPublisher = publisher;
        break;
      }
    }
    return accessLogPublisher != null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationDelete(
      AccessLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    AccessLogPublisher<?> accessLogPublisher = null;
    for(AccessLogPublisher<?> publisher : accessPublishers)
    {
      if(publisher.getDN().equals(config.dn()))
      {
        accessLogPublisher = publisher;
        break;
      }
    }
    if(accessLogPublisher != null)
    {
      removeAccessLogPublisher(accessLogPublisher);
    }
    else
    {
      resultCode = ResultCode.NO_SUCH_OBJECT;
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  @SuppressWarnings("unchecked")
  private boolean isJavaClassAcceptable(AccessLogPublisherCfg config,
                                        List<Message> unacceptableReasons)
  {
    String className = config.getJavaClass();
    AccessLogPublisherCfgDefn d = AccessLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a AccessLogPublisher.
      AccessLogPublisher<AccessLogPublisherCfg> publisher =
          pd.loadClass(className, AccessLogPublisher.class).newInstance();
      // The class is valid as far as we can tell.
      return publisher.isConfigurationAcceptable(config, unacceptableReasons);
    } catch (Exception e) {
      Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
          className,
          config.dn().toString(),
          String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
  }
  private AccessLogPublisher<?> getAccessPublisher(AccessLogPublisherCfg config)
      throws ConfigException {
    String className = config.getJavaClass();
    AccessLogPublisherCfgDefn d = AccessLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a AccessLogPublisher.
      AccessLogPublisher<AccessLogPublisherCfg> accessLogPublisher =
          pd.loadClass(className, AccessLogPublisher.class).newInstance();
      accessLogPublisher.initializeLogPublisher(config);
      // The access publisher has been successfully initialized.
      return accessLogPublisher;
    }
    catch (Exception e)
    {
      Message message = ERR_CONFIG_LOGGER_INVALID_ACCESS_LOGGER_CLASS.get(
          className, config.dn().toString(), String.valueOf(e));
      throw new ConfigException(message, e);
    }
  }
  /**
   * Writes a message to the access logger with information about a new client
@@ -396,7 +143,7 @@
   */
  public static void logConnect(ClientConnection clientConnection)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logConnect(clientConnection);
    }
@@ -418,7 +165,7 @@
                                   DisconnectReason disconnectReason,
                                   Message message)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logDisconnect(clientConnection, disconnectReason, message);
    }
@@ -435,7 +182,7 @@
   */
  public static void logAbandonRequest(AbandonOperation abandonOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logAbandonRequest(abandonOperation);
    }
@@ -452,7 +199,7 @@
   */
  public static void logAbandonResult(AbandonOperation abandonOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logAbandonResult(abandonOperation);
    }
@@ -469,7 +216,7 @@
   */
  public static void logAddRequest(AddOperation addOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logAddRequest(addOperation);
    }
@@ -486,7 +233,7 @@
   */
  public static void logAddResponse(AddOperation addOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logAddResponse(addOperation);
    }
@@ -503,7 +250,7 @@
   */
  public static void logBindRequest(BindOperation bindOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logBindRequest(bindOperation);
    }
@@ -520,7 +267,7 @@
   */
  public static void logBindResponse(BindOperation bindOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logBindResponse(bindOperation);
    }
@@ -537,7 +284,7 @@
   */
  public static void logCompareRequest(CompareOperation compareOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logCompareRequest(compareOperation);
    }
@@ -554,7 +301,7 @@
   */
  public static void logCompareResponse(CompareOperation compareOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logCompareResponse(compareOperation);
    }
@@ -571,7 +318,7 @@
   */
  public static void logDeleteRequest(DeleteOperation deleteOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logDeleteRequest(deleteOperation);
    }
@@ -588,7 +335,7 @@
   */
  public static void logDeleteResponse(DeleteOperation deleteOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logDeleteResponse(deleteOperation);
    }
@@ -605,7 +352,7 @@
   */
  public static void logExtendedRequest(ExtendedOperation extendedOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logExtendedRequest(extendedOperation);
    }
@@ -622,7 +369,7 @@
   */
  public static void logExtendedResponse(ExtendedOperation extendedOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logExtendedResponse(extendedOperation);
    }
@@ -639,7 +386,7 @@
   */
  public static void logModifyRequest(ModifyOperation modifyOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logModifyRequest(modifyOperation);
    }
@@ -656,7 +403,7 @@
   */
  public static void logModifyResponse(ModifyOperation modifyOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logModifyResponse(modifyOperation);
    }
@@ -673,7 +420,7 @@
   */
  public static void logModifyDNRequest(ModifyDNOperation modifyDNOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logModifyDNRequest(modifyDNOperation);
    }
@@ -691,7 +438,7 @@
   */
  public static void logModifyDNResponse(ModifyDNOperation modifyDNOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logModifyDNResponse(modifyDNOperation);
    }
@@ -708,7 +455,7 @@
   */
  public static void logSearchRequest(SearchOperation searchOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logSearchRequest(searchOperation);
    }
@@ -728,7 +475,7 @@
  public static void logSearchResultEntry(SearchOperation searchOperation,
                                          SearchResultEntry searchEntry)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logSearchResultEntry(searchOperation, searchEntry);
    }
@@ -747,7 +494,7 @@
  public static void logSearchResultReference(SearchOperation searchOperation,
                          SearchResultReference searchReference)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logSearchResultReference(searchOperation, searchReference);
    }
@@ -764,7 +511,7 @@
   */
  public static void logSearchResultDone(SearchOperation searchOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logSearchResultDone(searchOperation);
    }
@@ -781,7 +528,7 @@
   */
  public static void logUnbind(UnbindOperation unbindOperation)
  {
    for (AccessLogPublisher<?> publisher : accessPublishers)
    for (AccessLogPublisher<?> publisher : getAccessLogPublishers())
    {
      publisher.logUnbind(unbindOperation);
    }
opends/src/server/org/opends/server/loggers/ErrorLogger.java
@@ -28,53 +28,30 @@
package org.opends.server.loggers;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.opends.messages.Message;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.meta.ErrorLogPublisherCfgDefn;
import org.opends.server.admin.std.server.ErrorLogPublisherCfg;
import org.opends.server.api.DirectoryThread;
import org.opends.server.api.ErrorLogPublisher;
import org.opends.server.backends.task.Task;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
/**
 * This class defines the wrapper that will invoke all registered error loggers
 * for each type of request received or response sent. If no error log
 * publishers are registered, messages will be directed to standard out.
 */
public class ErrorLogger implements
    ConfigurationAddListener<ErrorLogPublisherCfg>,
    ConfigurationDeleteListener<ErrorLogPublisherCfg>,
    ConfigurationChangeListener<ErrorLogPublisherCfg>
public class ErrorLogger extends AbstractLogger
    <ErrorLogPublisher<ErrorLogPublisherCfg>, ErrorLogPublisherCfg>
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // The set of error loggers that have been registered with the server. It
  // will initially be empty.
  private static CopyOnWriteArrayList<ErrorLogPublisher> errorPublishers =
      new CopyOnWriteArrayList<ErrorLogPublisher>();
  private static LoggerStorage
      <ErrorLogPublisher<ErrorLogPublisherCfg>, ErrorLogPublisherCfg>
      loggerStorage = new LoggerStorage
      <ErrorLogPublisher<ErrorLogPublisherCfg>, ErrorLogPublisherCfg>();
  // The singleton instance of this class for configuration purposes.
  /** The singleton instance of this class for configuration purposes. */
  private static final ErrorLogger instance = new ErrorLogger();
  /**
@@ -88,6 +65,31 @@
  }
  /**
   * The constructor for this class.
   */
  public ErrorLogger()
  {
    super((Class) ErrorLogPublisher.class,
        ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS);
  }
  /** {@inheritDoc} */
  @Override
  protected ClassPropertyDefinition getJavaClassPropertyDefinition()
  {
    return ErrorLogPublisherCfgDefn.getInstance()
        .getJavaClassPropertyDefinition();
  }
  /** {@inheritDoc} */
  @Override
  protected LoggerStorage<ErrorLogPublisher<ErrorLogPublisherCfg>,
      ErrorLogPublisherCfg> getStorage()
  {
    return loggerStorage;
  }
  /**
   * Add an error log publisher to the error logger.
   *
   * @param publisher The error log publisher to add.
@@ -95,7 +97,7 @@
  public synchronized static void addErrorLogPublisher(
      ErrorLogPublisher publisher)
  {
    errorPublishers.add(publisher);
    loggerStorage.addLogPublisher(publisher);
  }
  /**
@@ -107,14 +109,7 @@
  public synchronized static boolean removeErrorLogPublisher(
      ErrorLogPublisher publisher)
  {
    boolean removed = errorPublishers.remove(publisher);
    if(removed)
    {
      publisher.close();
    }
    return removed;
    return loggerStorage.removeLogPublisher(publisher);
  }
  /**
@@ -122,278 +117,17 @@
   */
  public synchronized static void removeAllErrorLogPublishers()
  {
    for(ErrorLogPublisher publisher : errorPublishers)
    {
      publisher.close();
    }
    errorPublishers.clear();
    loggerStorage.removeAllLogPublishers();
  }
  /**
   * Initializes all the error log publishers.
   *
   * @param configs The error log publisher configurations.
   * @throws ConfigException
   *           If an unrecoverable problem arises in the process of
   *           performing the initialization as a result of the server
   *           configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization that is not
   *           related to the server configuration.
   */
  public void initializeErrorLogger(List<ErrorLogPublisherCfg> configs)
      throws ConfigException, InitializationException
  {
    for(ErrorLogPublisherCfg config : configs)
    {
      config.addErrorChangeListener(this);
      if(config.isEnabled())
      {
        ErrorLogPublisher errorLogPublisher = getErrorPublisher(config);
        addErrorLogPublisher(errorLogPublisher);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAddAcceptable(ErrorLogPublisherCfg config,
                                              List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationChangeAcceptable(
          ErrorLogPublisherCfg config,
          List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationAdd(ErrorLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    config.addErrorChangeListener(this);
    if(config.isEnabled())
    {
      try
      {
        ErrorLogPublisher errorLogPublisher = getErrorPublisher(config);
        addErrorLogPublisher(errorLogPublisher);
      }
      catch(ConfigException e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
                String.valueOf(config.dn().toString()),
                stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationChange(
      ErrorLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    DN dn = config.dn();
    ErrorLogPublisher errorLogPublisher = null;
    for(ErrorLogPublisher publisher : errorPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        errorLogPublisher = publisher;
        break;
      }
    }
    if(errorLogPublisher == null)
    {
      if(config.isEnabled())
      {
        // Needs to be added and enabled.
        return applyConfigurationAdd(config);
      }
    }
    else
    {
      if(config.isEnabled())
      {
        // The publisher is currently active, so we don't need to do anything.
        // Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = config.getJavaClass();
        if(!className.equals(errorLogPublisher.getClass().getName()))
        {
          adminActionRequired = true;
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        removeErrorLogPublisher(errorLogPublisher);
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationDeleteAcceptable(
          ErrorLogPublisherCfg config,
          List<Message> unacceptableReasons)
  {
    DN dn = config.dn();
    ErrorLogPublisher errorLogPublisher = null;
    for(ErrorLogPublisher publisher : errorPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        errorLogPublisher = publisher;
        break;
      }
    }
    return errorLogPublisher != null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationDelete(
      ErrorLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ErrorLogPublisher errorLogPublisher = null;
    for(ErrorLogPublisher publisher : errorPublishers)
    {
      if(publisher.getDN().equals(config.dn()))
      {
        errorLogPublisher = publisher;
        break;
      }
    }
    if(errorLogPublisher != null)
    {
      removeErrorLogPublisher(errorLogPublisher);
    }
    else
    {
      resultCode = ResultCode.NO_SUCH_OBJECT;
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  private boolean isJavaClassAcceptable(ErrorLogPublisherCfg config,
                                        List<Message> unacceptableReasons)
  {
    String className = config.getJavaClass();
    ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a ErrorLogPublisher.
      ErrorLogPublisher<ErrorLogPublisherCfg> publisher =
          pd.loadClass(className, ErrorLogPublisher.class).newInstance();
      // The class is valid as far as we can tell.
      return publisher.isConfigurationAcceptable(config, unacceptableReasons);
    } catch (Exception e) {
      Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
              className,
              config.dn().toString(),
              String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
  }
  private ErrorLogPublisher getErrorPublisher(ErrorLogPublisherCfg config)
      throws ConfigException {
    String className = config.getJavaClass();
    ErrorLogPublisherCfgDefn d = ErrorLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a ErrorLogPublisher.
      ErrorLogPublisher<ErrorLogPublisherCfg> errorLogPublisher =
          pd.loadClass(className, ErrorLogPublisher.class).newInstance();
      errorLogPublisher.initializeLogPublisher(config);
      // The error publisher has been successfully initialized.
      return errorLogPublisher;
    }
    catch (Exception e)
    {
      Message message = ERR_CONFIG_LOGGER_INVALID_ERROR_LOGGER_CLASS.get(
          className, config.dn().toString(), String.valueOf(e));
      throw new ConfigException(message, e);
    }
  }
  /**
   * Writes a message to the error log using the provided information.
   *
   * @param  message   The message to be logged.
   */
  public static void logError(Message message)
  {
    for (ErrorLogPublisher publisher : errorPublishers)
    for (ErrorLogPublisher publisher : loggerStorage.getLogPublishers())
    {
      publisher.logError(message);
    }
@@ -409,4 +143,3 @@
    }
  }
}
opends/src/server/org/opends/server/loggers/debug/DebugLogger.java
@@ -26,30 +26,19 @@
 *      Portions Copyright 2013 ForgeRock AS
 */
package org.opends.server.loggers.debug;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.opends.messages.Message;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.meta.DebugLogPublisherCfgDefn;
import org.opends.server.admin.std.server.DebugLogPublisherCfg;
import org.opends.server.api.DebugLogPublisher;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.AbstractLogger;
import org.opends.server.loggers.LogLevel;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
/**
 * A logger for debug and trace logging. DebugLogger provides a debugging
@@ -63,46 +52,71 @@
 *
 * DebugLogger is self-initializing.
 */
public class DebugLogger implements
    ConfigurationAddListener<DebugLogPublisherCfg>,
    ConfigurationDeleteListener<DebugLogPublisherCfg>,
    ConfigurationChangeListener<DebugLogPublisherCfg>
public class DebugLogger extends AbstractLogger
    <DebugLogPublisher<DebugLogPublisherCfg>, DebugLogPublisherCfg>
{
  //The default level to log constructor exectuions.
  /** The default level to log constructor executions. */
  static final LogLevel DEFAULT_CONSTRUCTOR_LEVEL =
      DebugLogLevel.VERBOSE;
  //The default level to log method entry and exit pointcuts.
  /** The default level to log method entry and exit pointcuts. */
  static final LogLevel DEFAULT_ENTRY_EXIT_LEVEL =
      DebugLogLevel.VERBOSE;
  //The default level to log method entry and exit pointcuts.
  /** The default level to log method entry and exit pointcuts. */
  static final LogLevel DEFAULT_THROWN_LEVEL =
      DebugLogLevel.ERROR;
  // The set of all DebugTracer instances.
  private static ConcurrentHashMap<String, DebugTracer> classTracers =
  /** The set of all DebugTracer instances. */
  private static Map<String, DebugTracer> classTracers =
      new ConcurrentHashMap<String, DebugTracer>();
  // The set of debug loggers that have been registered with the server.  It
  // will initially be empty.
  private static CopyOnWriteArrayList<DebugLogPublisher> debugPublishers =
      new CopyOnWriteArrayList<DebugLogPublisher>();
  // Trace methods will use this static boolean to determine if debug is
  // enabled so to not incur the cost of calling debugPublishers.isEmtpty().
  /**
   * Trace methods will use this static boolean to determine if debug is enabled
   * so to not incur the cost of calling debugPublishers.isEmpty().
   */
  static boolean enabled = false;
  // The singleton instance of this class for configuration purposes.
  private static final LoggerStorage
      <DebugLogPublisher<DebugLogPublisherCfg>, DebugLogPublisherCfg>
      loggerStorage = new LoggerStorage
      <DebugLogPublisher<DebugLogPublisherCfg>, DebugLogPublisherCfg>();
  /** The singleton instance of this class for configuration purposes. */
  static final DebugLogger instance = new DebugLogger();
  /**
   * The constructor for this class.
   */
  public DebugLogger()
  {
    super((Class) DebugLogPublisher.class,
        ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS);
  }
  /** {@inheritDoc} */
  @Override
  protected ClassPropertyDefinition getJavaClassPropertyDefinition()
  {
    return DebugLogPublisherCfgDefn.getInstance()
        .getJavaClassPropertyDefinition();
  }
  /** {@inheritDoc} */
  @Override
  protected LoggerStorage<DebugLogPublisher<DebugLogPublisherCfg>,
      DebugLogPublisherCfg> getStorage()
  {
    return loggerStorage;
  }
  /**
   * Add an debug log publisher to the debug logger.
   *
   * @param publisher The error log publisher to add.
   * @param publisher The debug log publisher to add.
   */
  public synchronized static void addDebugLogPublisher(
      DebugLogPublisher publisher)
  {
    debugPublishers.add(publisher);
    loggerStorage.addLogPublisher(publisher);
    updateTracerSettings();
@@ -118,19 +132,11 @@
  public synchronized static boolean removeDebugLogPublisher(
      DebugLogPublisher publisher)
  {
    boolean removed = debugPublishers.remove(publisher);
    if(removed)
    {
      publisher.close();
    }
    boolean removed = loggerStorage.removeLogPublisher(publisher);
    updateTracerSettings();
    if(debugPublishers.isEmpty())
    {
      enabled = false;
    }
    enabled = !loggerStorage.getLogPublishers().isEmpty();
    return removed;
  }
@@ -140,12 +146,7 @@
   */
  public synchronized static void removeAllDebugLogPublishers()
  {
    for(DebugLogPublisher publisher : debugPublishers)
    {
      publisher.close();
    }
    debugPublishers.clear();
    loggerStorage.removeAllLogPublishers();
    updateTracerSettings();
@@ -153,256 +154,13 @@
  }
  /**
   * Initializes all the debug log publishers.
   *
   * @param  configs The debug log publisher configurations.
   * @throws ConfigException
   *           If an unrecoverable problem arises in the process of
   *           performing the initialization as a result of the server
   *           configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization that is not
   *           related to the server configuration.
   */
  public void initializeDebugLogger(List<DebugLogPublisherCfg> configs)
      throws ConfigException, InitializationException
  {
    for(DebugLogPublisherCfg config : configs)
    {
      config.addDebugChangeListener(this);
      if(config.isEnabled())
      {
        DebugLogPublisher debugLogPublisher = getDebugPublisher(config);
        addDebugLogPublisher(debugLogPublisher);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationAddAcceptable(DebugLogPublisherCfg config,
                                              List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationChangeAcceptable(DebugLogPublisherCfg config,
                                              List<Message> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationAdd(DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    config.addDebugChangeListener(this);
    if(config.isEnabled())
    {
      try
      {
        DebugLogPublisher debugLogPublisher =
            getDebugPublisher(config);
        addDebugLogPublisher(debugLogPublisher);
      }
      catch(ConfigException e)
      {
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (Exception e)
      {
        messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get(
                String.valueOf(config.dn().toString()),
                stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult applyConfigurationChange(
      DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    DN dn = config.dn();
    DebugLogPublisher debugLogPublisher = null;
    for(DebugLogPublisher publisher : debugPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        debugLogPublisher = publisher;
      }
    }
    if(debugLogPublisher == null)
    {
      if(config.isEnabled())
      {
        // Needs to be added and enabled.
        return applyConfigurationAdd(config);
      }
    }
    else
    {
      if(config.isEnabled())
      {
        // The publisher is currently active, so we don't need to do anything.
        // Changes to the class name cannot be
        // applied dynamically, so if the class name did change then
        // indicate that administrative action is required for that
        // change to take effect.
        String className = config.getJavaClass();
        if(!className.equals(debugLogPublisher.getClass().getName()))
        {
          adminActionRequired = true;
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        removeDebugLogPublisher(debugLogPublisher);
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isConfigurationDeleteAcceptable(DebugLogPublisherCfg config,
                                             List<Message> unacceptableReasons)
  {
    DN dn = config.dn();
    DebugLogPublisher debugLogPublisher = null;
    for(DebugLogPublisher publisher : debugPublishers)
    {
      if(publisher.getDN().equals(dn))
      {
        debugLogPublisher = publisher;
      }
    }
    return debugLogPublisher != null;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ConfigChangeResult
         applyConfigurationDelete(DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    DebugLogPublisher debugLogPublisher = null;
    for(DebugLogPublisher publisher : debugPublishers)
    {
      if(publisher.getDN().equals(config.dn()))
      {
        debugLogPublisher = publisher;
      }
    }
    if(debugLogPublisher != null)
    {
      removeDebugLogPublisher(debugLogPublisher);
    }
    else
    {
      resultCode = ResultCode.NO_SUCH_OBJECT;
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  private boolean isJavaClassAcceptable(DebugLogPublisherCfg config,
                                        List<Message> unacceptableReasons)
  {
    String className = config.getJavaClass();
    DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a DebugLogPublisher.
      DebugLogPublisher<DebugLogPublisherCfg> publisher =
          pd.loadClass(className, DebugLogPublisher.class).newInstance();
      // The class is valid as far as we can tell.
      return publisher.isConfigurationAcceptable(config, unacceptableReasons);
    } catch (Exception e) {
      Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get(
              className,
              config.dn().toString(),
              String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
  }
  private DebugLogPublisher getDebugPublisher(DebugLogPublisherCfg config)
      throws ConfigException {
    String className = config.getJavaClass();
    DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaClassPropertyDefinition();
    try {
      // Load the class and cast it to a DebugLogPublisher.
      DebugLogPublisher<DebugLogPublisherCfg> debugLogPublisher =
          pd.loadClass(className, DebugLogPublisher.class).newInstance();
      debugLogPublisher.initializeLogPublisher(config);
      // The debug publisher has been successfully initialized.
      return debugLogPublisher;
    }
    catch (Exception e)
    {
      Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get(
          className, config.dn().toString(), String.valueOf(e));
      throw new ConfigException(message, e);
    }
  }
  /**
   * Update all debug tracers with the settings in the registered
   * publishers.
   */
  static void updateTracerSettings()
  {
    DebugLogPublisher[] publishers =
        debugPublishers.toArray(new DebugLogPublisher[0]);
    DebugLogPublisher<DebugLogPublisherCfg>[] publishers =
        loggerStorage.getLogPublishers().toArray(new DebugLogPublisher[0]);
    for(DebugTracer tracer : classTracers.values())
    {
@@ -439,7 +197,8 @@
  public static DebugTracer getTracer()
  {
    DebugTracer tracer =
        new DebugTracer(debugPublishers.toArray(new DebugLogPublisher[0]));
        new DebugTracer(loggerStorage.getLogPublishers().toArray(
            new DebugLogPublisher[0]));
    classTracers.put(tracer.getTracedClassName(), tracer);
    return tracer;
opends/src/server/org/opends/server/util/StaticUtils.java
@@ -51,6 +51,7 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
@@ -4622,6 +4623,25 @@
   */
  public static void close(Closeable... closeables)
  {
    if (closeables == null)
    {
      return;
    }
    close(Arrays.asList(closeables));
  }
  /**
   * Closes the provided {@link Closeable}'s ignoring any errors which occurred.
   *
   * @param closeables
   *          The closeables to be closed, which may be <code>null</code>.
   */
  public static void close(Collection<Closeable> closeables)
  {
    if (closeables == null)
    {
      return;
    }
    for (Closeable closeable : closeables)
    {
      if (closeable != null)