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

boli
03.55.2007 6dc91f889744c17a9a0d8a7c92276b62dea25941
opendj-sdk/opends/src/server/org/opends/server/loggers/debug/DebugLogger.java
@@ -22,22 +22,38 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.loggers.debug;
import org.opends.server.api.ProtocolElement;
import org.opends.server.loggers.Logger;
import org.opends.server.loggers.LogLevel;
import java.util.Map;
import java.util.HashMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.nio.ByteBuffer;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Database;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.DatabaseEntry;
import org.opends.server.api.ProtocolElement;
import org.opends.server.api.DebugLogPublisher;
import org.opends.server.loggers.*;
import org.opends.server.types.*;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.StaticUtils;
import org.opends.server.admin.std.server.DebugLogPublisherCfg;
import org.opends.server.admin.std.meta.DebugLogPublisherCfgDefn;
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.ClassPropertyDefinition;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.ConfigMessages.*;
import static org.opends.server.util.StaticUtils.*;
import com.sleepycat.je.*;
/**
 * A logger for debug and trace logging. DebugLogger provides a debugging
@@ -51,85 +67,446 @@
 *
 * DebugLogger is self-initializing.
 */
public class DebugLogger extends Logger
public class DebugLogger implements
    ConfigurationAddListener<DebugLogPublisherCfg>,
    ConfigurationDeleteListener<DebugLogPublisherCfg>,
    ConfigurationChangeListener<DebugLogPublisherCfg>
{
  private static DebugLogger logger = null;
  static boolean staticEnabled = false;
  //The default level to log constructor exectuions.
  static final LogLevel DEFAULT_CONSTRUCTOR_LEVEL =
      DebugLogLevel.VERBOSE;
  //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.
  static final LogLevel DEFAULT_THROWN_LEVEL =
      DebugLogLevel.ERROR;
  private Map<String, Tracer> classTracers;
  // The set of all DebugTracer aspect instances.
  static CopyOnWriteArraySet<DebugTracer> classTracers =
      new CopyOnWriteArraySet<DebugTracer>();
  private DebugConfiguration configuration;
  // The set of debug loggers that have been registered with the server.  It
  // will initially be empty.
  static ConcurrentHashMap<DN,
      DebugLogPublisher> debugPublishers =
      new ConcurrentHashMap<DN,
          DebugLogPublisher>();
  private DebugLogger(DebugConfiguration config)
  // Trace methods will use this static boolean to determine if debug is
  // enabled so to not incur the cost of calling debugPublishers.isEmtpty().
  static boolean enabled = false;
  // The singleton instance of this class for configuration purposes.
  static final DebugLogger instance = new DebugLogger();
  static
  {
    super(config);
    configuration = config;
    classTracers = new HashMap<String, Tracer>();
    staticEnabled = enabled;
    // Install the startup publishers if necessary until the config kicks in and
    // adds the real publishers.
    if(DynamicConstants.WEAVE_ENABLED)
    {
      try
      {
        TextDebugLogPublisher startupDebugPublisher =
            TextDebugLogPublisher.getStartupTextDebugPublisher(
                new TextWriter.STDOUT());
        debugPublishers.put(DN.NULL_DN,
                            startupDebugPublisher);
        enabled = true;
        // Update all existing aspect instances
        addTracerSettings(startupDebugPublisher);
      }
      catch(Exception e)
      {
        System.err.println("Error installing the startup debug logger: " +
            StaticUtils.stackTraceToSingleLineString(e));
      }
    }
  }
  /**
   * Add an debug log publisher to the debug logger.
   *
   * @param dn The DN of the configuration entry for the publisher.
   * @param publisher The error log publisher to add.
   */
  public synchronized static void addDebugLogPublisher(DN dn,
                                                 DebugLogPublisher publisher)
  {
    debugPublishers.put(dn, publisher);
    enabled = DynamicConstants.WEAVE_ENABLED;
  }
  /**
   * Obtain the trace logger singleton.
   * @return the trace logger singleton.
   * Remove an debug log publisher from the debug logger.
   *
   * @param dn The DN of the publisher to remove.
   * @return The publisher that was removed or null if it was not found.
   */
  public static synchronized DebugLogger getLogger()
  public synchronized static DebugLogPublisher removeDebugLogPublisher(DN dn)
  {
    if (logger == null) {
      /**
       * The debug logger is being intialized for the first time.
       * Bootstrap the debug logger when the server first starts up so
       * all debug messages are log from the first initialization of a
       * server class.
       */
      logger= new DebugLogger(DebugConfiguration.getStartupConfiguration());
    DebugLogPublisher removed =  debugPublishers.remove(dn);
    if(removed != null)
    {
      removed.close();
    }
    return logger;
    if(debugPublishers.isEmpty())
    {
      enabled = false;
    }
    return removed;
  }
  /**
   * Obtain the status of this logger singleton.
   * Removes all existing debug log publishers from the logger.
   */
  public synchronized static void removeAllDebugLogPublishers()
  {
    for(DebugLogPublisher publisher : debugPublishers.values())
    {
      publisher.close();
    }
    debugPublishers.clear();
    enabled = false;
  }
  /**
   * Initializes all the debug log publishers.
   *
   * @return the status of this logger.
   * @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(config.dn(), debugLogPublisher);
        // Update all existing aspect instances
        addTracerSettings(debugLogPublisher);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(DebugLogPublisherCfg config,
                                              List<String> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(DebugLogPublisherCfg config,
                                               List<String> unacceptableReasons)
  {
    return !config.isEnabled() ||
        isJavaClassAcceptable(config, unacceptableReasons);
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    config.addDebugChangeListener(this);
    if(config.isEnabled())
    {
      try
      {
        DebugLogPublisher debugLogPublisher =
            getDebugPublisher(config);
        addDebugLogPublisher(config.dn(), debugLogPublisher);
        addTracerSettings(debugLogPublisher);
      }
      catch(ConfigException e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(e.getMessage());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        int msgID = MSGID_CONFIG_LOGGER_CANNOT_CREATE_LOGGER;
        messages.add(getMessage(msgID, String.valueOf(config.dn().toString()),
                                stackTraceToSingleLineString(e)));
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
      DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<String> messages = new ArrayList<String>();
    DN dn = config.dn();
    DebugLogPublisher debugLogPublisher = debugPublishers.get(dn);
    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.getJavaImplementationClass();
        if(!className.equals(debugLogPublisher.getClass().getName()))
        {
          adminActionRequired = true;
        }
      }
      else
      {
        // The publisher is being disabled so shut down and remove.
        removeTracerSettings(debugLogPublisher);
        removeDebugLogPublisher(config.dn());
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(DebugLogPublisherCfg config,
                                               List<String> unacceptableReasons)
  {
    DN dn = config.dn();
    DebugLogPublisher debugLogPublisher = debugPublishers.get(dn);
    return debugLogPublisher != null;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult
         applyConfigurationDelete(DebugLogPublisherCfg config)
  {
    // Default result code.
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    DebugLogPublisher publisher = removeDebugLogPublisher(config.dn());
    if(publisher != null)
    {
      removeTracerSettings(publisher);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired);
  }
  private boolean isJavaClassAcceptable(DebugLogPublisherCfg config,
                                        List<String> unacceptableReasons)
  {
    String className = config.getJavaImplementationClass();
    DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaImplementationClassPropertyDefinition();
    // Load the class and cast it to a DebugLogPublisher.
    Class<? extends DebugLogPublisher> theClass;
    try {
      theClass = pd.loadClass(className, DebugLogPublisher.class);
      theClass.newInstance();
    } catch (Exception e) {
      int    msgID   = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS;
      String message = getMessage(msgID, className,
                                  config.dn().toString(),
                                  String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
    // Check that the implementation class implements the correct interface.
    try {
      // Determine the initialization method to use: it must take a
      // single parameter which is the exact type of the configuration
      // object.
      theClass.getMethod("initializeDebugLogPublisher", config.definition()
          .getServerConfigurationClass());
    } catch (Exception e) {
      int    msgID   = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS;
      String message = getMessage(msgID, className,
                                  config.dn().toString(),
                                  String.valueOf(e));
      unacceptableReasons.add(message);
      return false;
    }
    // The class is valid as far as we can tell.
    return true;
  }
  private DebugLogPublisher getDebugPublisher(DebugLogPublisherCfg config)
      throws ConfigException {
    String className = config.getJavaImplementationClass();
    DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance();
    ClassPropertyDefinition pd =
        d.getJavaImplementationClassPropertyDefinition();
    // Load the class and cast it to a DebugLogPublisher.
    Class<? extends DebugLogPublisher> theClass;
    DebugLogPublisher debugLogPublisher;
    try {
      theClass = pd.loadClass(className, DebugLogPublisher.class);
      debugLogPublisher = theClass.newInstance();
      // Determine the initialization method to use: it must take a
      // single parameter which is the exact type of the configuration
      // object.
      Method method = theClass.getMethod("initializeDebugLogPublisher",
                             config.definition().getServerConfigurationClass());
      method.invoke(debugLogPublisher, config);
    }
    catch (InvocationTargetException ite)
    {
      // Rethrow the exceptions thrown be the invoked method.
      Throwable e = ite.getTargetException();
      int    msgID   = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS;
      String message = getMessage(msgID, className,
                                  config.dn().toString(),
                                  stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    catch (Exception e)
    {
      int    msgID   = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS;
      String message = getMessage(msgID, className,
                                  config.dn().toString(),
                                  String.valueOf(e));
      throw new ConfigException(msgID, message, e);
    }
    // The debug publisher has been successfully initialized.
    return debugLogPublisher;
  }
  /**
   * Adds the settings for the provided publisher in all existing tracers.
   * If existing settings exist for the given publisher, it will be updated
   * with the new settings.
   *
   * @param publisher The debug log publisher with the new settings.
   */
  @SuppressWarnings("unchecked")
  public static void addTracerSettings(DebugLogPublisher publisher)
  {
    // Make sure this publisher is still registered with us. If not, don't
    // use its settings.
    if(debugPublishers.contains(publisher))
    {
      for(DebugTracer tracer : classTracers)
      {
        tracer.classSettings.put(publisher,
                                 publisher.getClassSettings(tracer.className));
        // For some reason, the compiler doesn't see that
        // debugLogPublihser.getMethodSettings returns a parameterized Map.
        // This problem goes away if a parameterized verson of
        // DebugLogPublisher is used. However, we can't not use reflection to
        // instantiate a generic
        // DebugLogPublisher<? extends DebugLogPublisherCfg> type. The only
        // thing we can do is to just suppress the compiler warnings.
        Map<String, TraceSettings> methodSettings =
            publisher.getMethodSettings(tracer.className);
        if(methodSettings != null)
        {
          tracer.methodSettings.put(publisher, methodSettings);
        }
      }
    }
  }
  /**
   * Removes the settings for the provided publisher in all existing tracers.
   *
   * @param publisher The debug log publisher to remove.
   */
  public static void removeTracerSettings(DebugLogPublisher publisher)
  {
    for(DebugTracer tracer : classTracers)
    {
      tracer.classSettings.remove(publisher);
      tracer.methodSettings.remove(publisher);
    }
  }
  /**
   * Indicates if debug logging is enabled.
   *
   * @return True if debug logging is enabled. False otherwise.
   */
  public static boolean debugEnabled()
  {
    return staticEnabled;
    return enabled;
  }
  /**
   * Register a trace logger for the specified class.
   * @param className - the class for which to register the tracer under.
   * @param tracer - the tracer object to register.
   */
  public synchronized void registerTracer(String className,
                                           Tracer tracer)
  {
    Tracer traceLogger = classTracers.get(className);
    if (traceLogger == null) {
      classTracers.put(className, tracer);
      tracer.updateSettings(this.configuration);
    }
  }
  /**
   * Update the tracing configuration of the debug logger with the specified
   * trace configuration.
   * Retrieve the singleton instance of this class.
   *
   * @param config the new configuration to apply.
   * @return The singleton instance of this logger.
   */
  public synchronized void updateConfiguration(DebugConfiguration config)
  public static DebugLogger getInstance()
  {
    super.updateConfiguration(config);
    staticEnabled = enabled;
    for(Tracer tracer : classTracers.values())
    {
      tracer.updateSettings(config);
    }
    this.configuration = config;
    return instance;
  }
  /**
@@ -305,5 +682,28 @@
                                          ProtocolElement element) {}
  /**
   * Classes and methods annotated with @NoDebugTracing will not be weaved with
   * debug logging statements by AspectJ.
   */
  public @interface NoDebugTracing {}
  /**
   * Methods annotated with @NoEntryDebugTracing will not be weaved with
   * entry debug logging statements by AspectJ.
   */
  public @interface NoEntryDebugTracing {}
  /**
   * Methods annotated with @NoExitDebugTracing will not be weaved with
   * exit debug logging statements by AspectJ.
   */
  public @interface NoExitDebugTracing {}
  /**
   * Methods annotated with @TraceThrown will be weaved by AspectJ with
   * debug logging statements when an exception is thrown from the method.
   */
  public @interface TraceThrown {}
}