From d755882f59202fe62b2ad5a141b3c044c1898aa6 Mon Sep 17 00:00:00 2001
From: boli <boli@localhost>
Date: Thu, 03 May 2007 21:55:23 +0000
Subject: [PATCH] Major changes made to the logging framework. It should resolve the following issues:

---
 opends/src/server/org/opends/server/loggers/debug/DebugLogger.java |  528 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 464 insertions(+), 64 deletions(-)

diff --git a/opends/src/server/org/opends/server/loggers/debug/DebugLogger.java b/opends/src/server/org/opends/server/loggers/debug/DebugLogger.java
index f00b353..2c203a9 100644
--- a/opends/src/server/org/opends/server/loggers/debug/DebugLogger.java
+++ b/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 {}
 
 }

--
Gitblit v1.10.0