qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ai...@apache.org
Subject svn commit: r747870 [1/2] - in /qpid/trunk/qpid/java/broker: etc/ src/main/java/org/apache/qpid/server/ src/main/java/org/apache/qpid/server/logging/ src/main/java/org/apache/qpid/server/logging/management/ src/main/java/org/apache/qpid/server/manageme...
Date Wed, 25 Feb 2009 17:53:10 GMT
Author: aidan
Date: Wed Feb 25 17:53:09 2009
New Revision: 747870

URL: http://svn.apache.org/viewvc?rev=747870&view=rev
Log:
QPID-1648: Add LoggingManagement and associated MBean to enable dynamic reloading of log4j file. Update sample log4js so that they aren't arbitrarily rewritten.

Patch from Robbie Gemmell <gemmellr@dcs.gla.ac.uk>

Added:
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagement.java
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
    qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/
    qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/
    qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/management/LoggingManagementMBeanTest.java
Modified:
    qpid/trunk/qpid/java/broker/etc/debug.log4j.xml
    qpid/trunk/qpid/java/broker/etc/log4j.xml
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
    qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java

Modified: qpid/trunk/qpid/java/broker/etc/debug.log4j.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/etc/debug.log4j.xml?rev=747870&r1=747869&r2=747870&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/etc/debug.log4j.xml (original)
+++ qpid/trunk/qpid/java/broker/etc/debug.log4j.xml Wed Feb 25 17:53:09 2009
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
  -
  - Licensed to the Apache Software Foundation (ASF) under one
@@ -18,10 +18,10 @@
  - specific language governing permissions and limitations
  - under the License.
  -
- -->
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-    <appender name="ArchivingFileAppender" class="org.apache.log4j.QpidCompositeRollingAppender">
+ --><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="null" threshold="null">
+    <appender class="org.apache.log4j.QpidCompositeRollingAppender" name="ArchivingFileAppender">
         <!-- Ensure that logs allways have the dateFormat set-->
         <param name="StaticLogFileName" value="false"/>
         <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
@@ -48,7 +48,7 @@
         </layout>
     </appender>
 
-    <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+    <appender class="org.apache.log4j.FileAppender" name="FileAppender">
         <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
         <param name="Append" value="false"/>
 
@@ -57,7 +57,7 @@
         </layout>
     </appender>
 
-    <appender name="AlertFile" class="org.apache.log4j.FileAppender">
+    <appender class="org.apache.log4j.FileAppender" name="AlertFile">
         <param name="File" value="${QPID_WORK}/log/alert.log"/>
         <param name="Append" value="false"/>
 
@@ -66,28 +66,28 @@
         </layout>
     </appender>
 
-    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+    <appender class="org.apache.log4j.ConsoleAppender" name="STDOUT">
 
         <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
         </layout>
     </appender>
 
-    <category name="Qpid.Broker">
+    <category additivity="true" name="Qpid.Broker">
         <priority value="debug"/>
         <appender-ref ref="AlertFile"/>
         <!--appender-ref ref="STDOUT"/-->        
     </category>
 
 
-    <category name="org.apache.qpid.server.queue.AMQQueueMBean">
+    <category additivity="true" name="org.apache.qpid.server.queue.AMQQueueMBean">
         <priority value="info"/>
         <appender-ref ref="AlertFile"/>
     </category>
 
 
     <!-- Provide warnings to standard output -->
-    <!--category name="org.apache.qpid">
+    <!--category additivity="true" name="org.apache.qpid">
         <priority value="warn"/>
         <appender-ref ref="STDOUT"/>
     </category-->
@@ -96,11 +96,11 @@
     <!-- Additional level settings for debugging -->
     <!-- Each class in the Broker is a category that can have its logging level adjusted. -->
     <!-- This will provide more details if available about that classes processing. -->
-    <!--category name="org.apache.qpid.server.txn">
+    <!--category additivity="true" name="org.apache.qpid.server.txn">
         <priority value="debug"/>
     </category>-->
 
-    <!--<category name="org.apache.qpid.server.store">
+    <!--<category additivity="true" name="org.apache.qpid.server.store">
         <priority value="debug"/>
     </category-->
 

Modified: qpid/trunk/qpid/java/broker/etc/log4j.xml
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/etc/log4j.xml?rev=747870&r1=747869&r2=747870&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/etc/log4j.xml (original)
+++ qpid/trunk/qpid/java/broker/etc/log4j.xml Wed Feb 25 17:53:09 2009
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
  -
  - Licensed to the Apache Software Foundation (ASF) under one
@@ -18,10 +18,10 @@
  - specific language governing permissions and limitations
  - under the License.
  -
- -->
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-    <appender name="ArchivingFileAppender" class="org.apache.log4j.QpidCompositeRollingAppender">
+ --><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="null" threshold="null">
+    <appender class="org.apache.log4j.QpidCompositeRollingAppender" name="ArchivingFileAppender">
         <!-- Ensure that logs allways have the dateFormat set-->
         <param name="StaticLogFileName" value="false"/>
         <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
@@ -48,7 +48,7 @@
         </layout>
     </appender>
 
-    <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+    <appender class="org.apache.log4j.FileAppender" name="FileAppender">
         <param name="File" value="${QPID_WORK}/log/${logprefix}qpid${logsuffix}.log"/>
         <param name="Append" value="false"/>
 
@@ -57,25 +57,25 @@
         </layout>
     </appender>
 
-    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+    <appender class="org.apache.log4j.ConsoleAppender" name="STDOUT">
 
         <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
         </layout>
     </appender>
 
-    <!-- Qpid.Broker log is a special log category used to only useful broker startup details -->
-    <category name="Qpid.Broker">
+    <!-- Qpid.Broker log is a special log category used to log only useful broker startup details -->
+    <category additivity="true" name="Qpid.Broker">
         <priority value="debug"/>
         <appender-ref ref="STDOUT"/>
     </category>
 
-    <category name="org.apache.qpid.server.queue.AMQQueueMBean">
+    <category additivity="true" name="org.apache.qpid.server.queue.AMQQueueMBean">
         <priority value="info"/>
     </category>
 
     <!-- Provide warnings to standard output -->
-    <category name="org.apache.qpid">
+    <category additivity="true" name="org.apache.qpid">
         <priority value="warn"/>
     </category>
 
@@ -83,7 +83,7 @@
     <!-- Examples of additional logging settings -->
     <!-- Used to generate extra debug. See debug.log4j.xml -->
     
-    <!--<category name="org.apache.qpid.server.store">
+    <!--<category additivity="true" name="org.apache.qpid.server.store">
         <priority value="debug"/>
     </category-->
 

Modified: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java?rev=747870&r1=747869&r2=747870&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java (original)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java Wed Feb 25 17:53:09 2009
@@ -42,10 +42,12 @@
 import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
 import org.apache.mina.transport.socket.nio.SocketSessionConfig;
 import org.apache.mina.util.NewThreadExecutor;
+import org.apache.qpid.AMQException;
 import org.apache.qpid.common.QpidProperties;
 import org.apache.qpid.framing.ProtocolVersion;
 import org.apache.qpid.pool.ReadWriteThreadModel;
 import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.management.LoggingManagementMBean;
 import org.apache.qpid.server.protocol.AMQPFastProtocolHandler;
 import org.apache.qpid.server.protocol.AMQPProtocolProvider;
 import org.apache.qpid.server.registry.ApplicationRegistry;
@@ -232,16 +234,29 @@
 
         String logConfig = commandLine.getOptionValue("l");
         String logWatchConfig = commandLine.getOptionValue("w", "0");
+        
+        int logWatchTime = 0;
+        try
+        {
+            logWatchTime = Integer.parseInt(logWatchConfig);
+        }
+        catch (NumberFormatException e)
+        {
+            System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be "
+                               + "a non-negative integer. Using default of zero (no watching configured");
+        }
+        
+        File logConfigFile;
         if (logConfig != null)
         {
-            File logConfigFile = new File(logConfig);
-            configureLogging(logConfigFile, logWatchConfig);
+            logConfigFile = new File(logConfig);
+            configureLogging(logConfigFile, logWatchTime);
         }
         else
         {
             File configFileDirectory = configFile.getParentFile();
-            File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME);
-            configureLogging(logConfigFile, logWatchConfig);
+            logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME);
+            configureLogging(logConfigFile, logWatchTime);
         }
 
         ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile);
@@ -249,6 +264,8 @@
         updateManagementPort(serverConfig, commandLine.getOptionValue("m"));
 
         ApplicationRegistry.initialise(config);
+        
+        configureLoggingManagementMBean(logConfigFile, logWatchTime);
 
         //fixme .. use QpidProperties.getVersionString when we have fixed the classpath issues
         // that are causing the broker build to pick up the wrong properties file and hence say
@@ -445,19 +462,8 @@
         return ip;
     }
 
-    private void configureLogging(File logConfigFile, String logWatchConfig)
+    private void configureLogging(File logConfigFile, int logWatchTime)
     {
-        int logWatchTime = 0;
-        try
-        {
-            logWatchTime = Integer.parseInt(logWatchConfig);
-        }
-        catch (NumberFormatException e)
-        {
-            System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be "
-                               + "a non-negative integer. Using default of zero (no watching configured");
-        }
-
         if (logConfigFile.exists() && logConfigFile.canRead())
         {
             System.out.println("Configuring logger using configuration file " + logConfigFile.getAbsolutePath());
@@ -481,4 +487,17 @@
         }
     }
 
+    private void configureLoggingManagementMBean(File logConfigFile, int logWatchTime) throws Exception
+    {
+        LoggingManagementMBean blm = new LoggingManagementMBean(logConfigFile.getPath(),logWatchTime);
+        
+        try
+        {
+            blm.register();
+        }
+        catch (AMQException e)
+        {
+            throw new InitException("Unable to initialise the Logging Management MBean: ", e);
+        }
+    }
 }

Added: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagement.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagement.java?rev=747870&view=auto
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagement.java (added)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagement.java Wed Feb 25 17:53:09 2009
@@ -0,0 +1,258 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.    
+ *
+ * 
+ */
+package org.apache.qpid.server.logging.management;
+
+import java.io.IOException;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.server.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+
+import javax.management.MBeanOperationInfo;
+import javax.management.openmbean.TabularData;
+
+public interface LoggingManagement
+{
+    String TYPE = "LoggingManagement";
+    int VERSION = 1;
+    
+    //TabularType and contained CompositeType key/description information
+    String[] COMPOSITE_ITEM_NAMES = {"LoggerName", "Level"};
+    String[] COMPOSITE_ITEM_DESCRIPTIONS = {"Name of the logger", "Level of the logger"};
+    String[] TABULAR_UNIQUE_INDEX = {COMPOSITE_ITEM_NAMES[0]};
+    
+    /**
+     * Attribute to represent the log4j xml configuration file's LogWatch interval.
+     * @return The logwatch interval in seconds.
+     */
+    @MBeanAttribute(name="Log4jLogWatchInterval", 
+                    description = "The log4j xml configuration file LogWatch interval (in seconds). 0 indicates not being checked.")
+    Integer getLog4jLogWatchInterval();
+    
+    
+    //****** log4j runtime operations ****** //
+
+    /**
+     * Sets the level of an active Log4J logger
+     * @param logger The name of the logger
+     * @param level The level to set the logger to
+     * @return True if successful, false if unsuccessful (eg if an invalid level is specified) 
+     */
+    @MBeanOperation(name = "setRuntimeLoggerLevel", description = "Set the runtime logging level for an active log4j logger.",
+                    impact = MBeanOperationInfo.ACTION)
+    boolean setRuntimeLoggerLevel(@MBeanOperationParameter(name = "logger", description = "Logger name")String logger,
+                                @MBeanOperationParameter(name = "level", description = "Logger level")String level);
+
+    /**
+     * Retrieves a TabularData set of the active log4j loggers and their levels
+     * @return TabularData set of CompositeData rows with logger name and level, or null if there is a problem with the TabularData type 
+     */
+    @MBeanOperation(name = "viewEffectiveRuntimeLoggerLevels", description = "View the effective runtime logging level " +
+    		        "for active log4j logger's.", impact = MBeanOperationInfo.INFO)
+    TabularData viewEffectiveRuntimeLoggerLevels();
+    
+    /**
+     * Sets the level of the active Log4J RootLogger
+     * @param level The level to set the RootLogger to
+     * @return True if successful, false if unsuccessful (eg if an invalid level is specified) 
+     */
+    @MBeanOperation(name = "setRuntimeRootLoggerLevel", description = "Set the runtime logging level for the active log4j Root Logger.",
+                    impact = MBeanOperationInfo.ACTION)
+    boolean setRuntimeRootLoggerLevel(@MBeanOperationParameter(name = "level", description = "Logger level")String level);
+
+    /**
+     * Attribute to represent the level of the active Log4J RootLogger
+     * @return The level of the RootLogger.
+     */
+    @MBeanAttribute(name = "getRuntimeRootLoggerLevel", description = "Get the runtime logging level for the active log4j Root Logger.")
+    String getRuntimeRootLoggerLevel();
+    
+    
+    //****** log4j XML configuration file operations ****** //
+    
+    /**
+     * Updates the level of an existing Log4J logger within the xml configuration file
+     * @param logger The name of the logger
+     * @param level The level to set the logger to
+     * @return True if successful, false if unsuccessful (eg if an invalid logger or level is specified) 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "setConfigFileLoggerLevel", description = "Set the logging level for an existing logger " +
+    		         "in the log4j xml configuration file", impact = MBeanOperationInfo.ACTION)
+    boolean setConfigFileLoggerLevel(@MBeanOperationParameter(name = "logger", description = "logger name")String logger,
+                                    @MBeanOperationParameter(name = "level", description = "Logger level")String level) throws IOException;
+    
+    /**
+     * Retrieves a TabularData set of the existing Log4J loggers within the xml configuration file
+     * @return TabularData set of CompositeData rows with logger name and level, or null if there is a problem with the TabularData type 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "viewConfigFileLoggerLevels", description = "Get the logging level defined for the logger's " +
+    		        "in the log4j xml configuration file.", impact = MBeanOperationInfo.INFO)
+    TabularData viewConfigFileLoggerLevels() throws IOException;
+ 
+    /**
+     * Updates the level of the Log4J RootLogger within the xml configuration file if it is present
+     * @param level The level to set the logger to
+     * @return True if successful, false if not (eg an invalid level is specified, or root logger level isnt already defined) 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "setConfigFileRootLoggerLevel", description = "Set the logging level for the Root Logger " +
+    		        "in the log4j xml configuration file.", impact = MBeanOperationInfo.ACTION)
+    boolean setConfigFileRootLoggerLevel(@MBeanOperationParameter(name = "level", description = "Logger level")String level) throws IOException;
+
+    /**
+     * Attribute to represent the level of the Log4J RootLogger within the xml configuration file
+     * @return The level of the RootLogger, or null if it is not present
+     */
+    @MBeanAttribute(name = "getConfigFileRootLoggerLevel", description = "Get the logging level for the Root Logger " +
+                    "in the log4j xml configuration file.")
+    String getConfigFileRootLoggerLevel() throws IOException;
+}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.    
+ *
+ * 
+ */
+package org.apache.qpid.server.logging.management;
+
+import java.io.IOException;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+import org.apache.qpid.server.management.MBeanOperation;
+import org.apache.qpid.server.management.MBeanOperationParameter;
+
+import javax.management.MBeanOperationInfo;
+import javax.management.openmbean.TabularData;
+
+public interface LoggingManagement
+{
+    String TYPE = "LoggingManagement";
+    int VERSION = 1;
+    
+    //TabularType and contained CompositeType key/description information
+    String[] COMPOSITE_ITEM_NAMES = {"LoggerName", "Level"};
+    String[] COMPOSITE_ITEM_DESCRIPTIONS = {"Name of the logger", "Level of the logger"};
+    String[] TABULAR_UNIQUE_INDEX = {COMPOSITE_ITEM_NAMES[0]};
+    
+    /**
+     * Attribute to represent the log4j xml configuration file's LogWatch interval.
+     * @return The logwatch interval in seconds.
+     */
+    @MBeanAttribute(name="Log4jLogWatchInterval", 
+                    description = "The log4j xml configuration file LogWatch interval (in seconds). 0 indicates not being checked.")
+    Integer getLog4jLogWatchInterval();
+    
+    
+    //****** log4j runtime operations ****** //
+
+    /**
+     * Sets the level of an active Log4J logger
+     * @param logger The name of the logger
+     * @param level The level to set the logger to
+     * @return True if successful, false if unsuccessful (eg if an invalid level is specified) 
+     */
+    @MBeanOperation(name = "setRuntimeLoggerLevel", description = "Set the runtime logging level for an active log4j logger.",
+                    impact = MBeanOperationInfo.ACTION)
+    boolean setRuntimeLoggerLevel(@MBeanOperationParameter(name = "logger", description = "Logger name")String logger,
+                                @MBeanOperationParameter(name = "level", description = "Logger level")String level);
+
+    /**
+     * Retrieves a TabularData set of the active log4j loggers and their levels
+     * @return TabularData set of CompositeData rows with logger name and level, or null if there is a problem with the TabularData type 
+     */
+    @MBeanOperation(name = "viewEffectiveRuntimeLoggerLevels", description = "View the effective runtime logging level " +
+    		        "for active log4j logger's.", impact = MBeanOperationInfo.INFO)
+    TabularData viewEffectiveRuntimeLoggerLevels();
+    
+    /**
+     * Sets the level of the active Log4J RootLogger
+     * @param level The level to set the RootLogger to
+     * @return True if successful, false if unsuccessful (eg if an invalid level is specified) 
+     */
+    @MBeanOperation(name = "setRuntimeRootLoggerLevel", description = "Set the runtime logging level for the active log4j Root Logger.",
+                    impact = MBeanOperationInfo.ACTION)
+    boolean setRuntimeRootLoggerLevel(@MBeanOperationParameter(name = "level", description = "Logger level")String level);
+
+    /**
+     * Attribute to represent the level of the active Log4J RootLogger
+     * @return The level of the RootLogger.
+     */
+    @MBeanAttribute(name = "getRuntimeRootLoggerLevel", description = "Get the runtime logging level for the active log4j Root Logger.")
+    String getRuntimeRootLoggerLevel();
+    
+    
+    //****** log4j XML configuration file operations ****** //
+    
+    /**
+     * Updates the level of an existing Log4J logger within the xml configuration file
+     * @param logger The name of the logger
+     * @param level The level to set the logger to
+     * @return True if successful, false if unsuccessful (eg if an invalid logger or level is specified) 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "setConfigFileLoggerLevel", description = "Set the logging level for an existing logger " +
+    		         "in the log4j xml configuration file", impact = MBeanOperationInfo.ACTION)
+    boolean setConfigFileLoggerLevel(@MBeanOperationParameter(name = "logger", description = "logger name")String logger,
+                                    @MBeanOperationParameter(name = "level", description = "Logger level")String level) throws IOException;
+    
+    /**
+     * Retrieves a TabularData set of the existing Log4J loggers within the xml configuration file
+     * @return TabularData set of CompositeData rows with logger name and level, or null if there is a problem with the TabularData type 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "viewConfigFileLoggerLevels", description = "Get the logging level defined for the logger's " +
+    		        "in the log4j xml configuration file.", impact = MBeanOperationInfo.INFO)
+    TabularData viewConfigFileLoggerLevels() throws IOException;
+ 
+    /**
+     * Updates the level of the Log4J RootLogger within the xml configuration file if it is present
+     * @param level The level to set the logger to
+     * @return True if successful, false if not (eg an invalid level is specified, or root logger level isnt already defined) 
+     * @throws IOException if there is an error parsing the configuration file.
+     */
+    @MBeanOperation(name = "setConfigFileRootLoggerLevel", description = "Set the logging level for the Root Logger " +
+    		        "in the log4j xml configuration file.", impact = MBeanOperationInfo.ACTION)
+    boolean setConfigFileRootLoggerLevel(@MBeanOperationParameter(name = "level", description = "Logger level")String level) throws IOException;
+
+    /**
+     * Attribute to represent the level of the Log4J RootLogger within the xml configuration file
+     * @return The level of the RootLogger, or null if it is not present
+     */
+    @MBeanAttribute(name = "getConfigFileRootLoggerLevel", description = "Get the logging level for the Root Logger " +
+                    "in the log4j xml configuration file.")
+    String getConfigFileRootLoggerLevel() throws IOException;
+}

Added: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java?rev=747870&view=auto
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java (added)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java Wed Feb 25 17:53:09 2009
@@ -0,0 +1,1334 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.    
+ *
+ * 
+ */
+package org.apache.qpid.server.logging.management;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.management.AMQManagedObject;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.Log4jEntityResolver;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+
+/** MBean class for BrokerLoggingManagerMBean. It implements all the management features exposed for managing logging. */
+@MBeanDescription("Logging Management Interface")
+public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement
+{
+
+    private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class);
+    private String _log4jConfigFileName;
+    private int _log4jLogWatchInterval;
+    
+    static TabularType _loggerLevelTabularType;
+    static CompositeType _loggerLevelCompositeType;
+
+    static
+    {
+        try
+        {
+            OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING};
+
+            _loggerLevelCompositeType = new CompositeType("LoggerLevelList", "Logger Level Data", 
+                                                         COMPOSITE_ITEM_NAMES, COMPOSITE_ITEM_DESCRIPTIONS, loggerLevelItemTypes);
+
+            _loggerLevelTabularType = new TabularType("LoggerLevel", "List of loggers with levels",
+                                                       _loggerLevelCompositeType, TABULAR_UNIQUE_INDEX);
+        }
+        catch (OpenDataException e)
+        {
+            _logger.error("Tabular data setup for viewing logger levels was incorrect.");
+            _loggerLevelTabularType = null;
+        }
+    }
+    
+    public LoggingManagementMBean(String log4jConfigFileName, int log4jLogWatchInterval) throws JMException
+    {
+        super(LoggingManagement.class, LoggingManagement.TYPE, LoggingManagement.VERSION);
+        _log4jConfigFileName = log4jConfigFileName;
+        _log4jLogWatchInterval = log4jLogWatchInterval;
+    }
+
+    public String getObjectInstanceName()
+    {
+        return LoggingManagement.TYPE;
+    }
+    
+    public Integer getLog4jLogWatchInterval()
+    {
+        return _log4jLogWatchInterval;
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized boolean setRuntimeLoggerLevel(String logger, String level)
+    {   
+        //check specified level is valid
+        Level newLevel;
+        try
+        {
+            newLevel = getLevel(level);
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+        
+        //check specified logger exists
+        Enumeration loggers = LogManager.getCurrentLoggers();
+        Boolean loggerExists = false;
+        
+        while(loggers.hasMoreElements())
+        {
+            Logger log = (Logger) loggers.nextElement();
+            if (log.getName().equals(logger))
+            {
+                loggerExists = true;
+                break;
+            }
+        }
+        
+        if(!loggerExists)
+        {
+            return false;
+        }
+        
+        //set the logger to the new level
+        _logger.info("Setting level to " + level + " for logger: " + logger);
+        
+        Logger log = Logger.getLogger(logger);
+        log.setLevel(newLevel);
+        
+        return true;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public synchronized TabularData viewEffectiveRuntimeLoggerLevels()
+    {
+        if (_loggerLevelTabularType == null)
+        {
+            _logger.warn("TabluarData type not set up correctly");
+            return null;
+        }
+
+        _logger.info("Getting levels for currently active log4j loggers");
+        
+        Enumeration loggers = LogManager.getCurrentLoggers();
+
+        TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+
+        Logger logger;
+        String loggerName;
+        String level;
+        
+        try
+        {
+            while(loggers.hasMoreElements()){
+                logger = (Logger) loggers.nextElement();
+
+                loggerName = logger.getName();
+                level = logger.getEffectiveLevel().toString();
+
+                Object[] itemData = {loggerName, level};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+        }
+        catch (OpenDataException e)
+        {
+            _logger.warn("Unable to create logger level list due to :" + e);
+            return null;
+        }
+
+        return loggerLevelList;
+        
+    }
+    
+    public synchronized String getRuntimeRootLoggerLevel()
+    {
+        Logger rootLogger = Logger.getRootLogger();
+
+        return rootLogger.getLevel().toString();
+    }
+
+    public synchronized boolean setRuntimeRootLoggerLevel(String level)
+    {
+            Level newLevel;
+            try
+            {
+                newLevel = getLevel(level);
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+            
+            _logger.info("Setting RootLogger level to " + level);
+            
+            Logger log = Logger.getRootLogger();
+            log.setLevel(newLevel);
+            
+            return true;
+    }
+    
+    //method to convert from a string to a log4j Level, throws exception if the given value is invalid
+    private Level getLevel(String level) throws Exception
+    {
+        Level newLevel = Level.toLevel(level);
+        
+        //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
+        if (newLevel.equals(Level.DEBUG) && !(level.equalsIgnoreCase("debug")))
+        {
+            //received DEBUG but we did not ask for it, the Level request failed.
+            throw new Exception("Invalid level name");
+        }
+        
+        return newLevel;
+    }
+    
+    //handler to catch errors signalled by the JAXP parser and throw an appropriate exception
+    private class SaxErrorHandler implements ErrorHandler
+    {
+        
+        public void error(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Error parsing XML file: " + e.getMessage());
+        }
+
+        public void fatalError(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Fatal error parsing XML file: " + e.getMessage());
+        }
+
+        public void warning(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Warning parsing XML file: " + e.getMessage());
+        }
+    }
+
+    //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
+    private synchronized Document parseConfigFile(String fileName) throws IOException
+    {
+        //check file was specified, exists, and is readable
+        if(fileName == null)
+        {
+            _logger.warn("No log4j XML configuration file has been set");
+            throw new IOException("No log4j XML configuration file has been set");
+        }
+
+        File configFile = new File(fileName);
+
+        if (!configFile.exists())
+        {
+            _logger.warn("Specified log4j XML configuration file does not exist: " + fileName);
+            throw new IOException("Specified log4j XML configuration file does not exist");
+        }
+        else if (!configFile.canRead())
+        {
+            _logger.warn("Specified log4j XML configuration file is not readable: " + fileName);
+            throw new IOException("Specified log4j XML configuration file is not readable");
+        }
+
+        //parse it
+        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder docBuilder;
+        Document doc;
+
+        ErrorHandler errHandler = new SaxErrorHandler();
+        try
+        {
+            docFactory.setValidating(true);
+            docBuilder = docFactory.newDocumentBuilder();
+            docBuilder.setErrorHandler(errHandler);
+            docBuilder.setEntityResolver(new Log4jEntityResolver());
+            doc = docBuilder.parse(fileName);
+        }
+        catch (ParserConfigurationException e)
+        {
+            _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e);
+            //recommended that MBeans should use java.* and javax.* exceptions only
+            throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage());
+        }
+        catch (SAXException e)
+        {
+            _logger.warn("The specified log4j XML file is invalid: " + e);
+            //recommended that MBeans should use standard java.* and javax.* exceptions only
+            throw new IOException("The specified log4j XML file is invalid: " + e.getMessage());
+        }
+        catch (IOException e)
+        {
+            _logger.warn("Unable to parse the specified log4j XML file" + e);
+            throw new IOException("Unable to parse the specified log4j XML file", e);
+        }
+
+        return doc;
+    }
+
+    
+    private synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
+    {
+        File log4jConfigFile = new File(log4jConfigFileName);
+        
+        if (!log4jConfigFile.canWrite())
+        {
+            _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
+            throw new IOException("Specified log4j XML configuration file is not writable");
+        }
+
+        Transformer transformer = null;
+        try
+        {
+            transformer = TransformerFactory.newInstance().newTransformer();
+        }
+        catch (Exception e)
+        {
+            _logger.warn("Could not create an XML transformer: " +e);
+            return false;
+        }
+
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
+        DOMSource source = new DOMSource(doc);
+        
+        File tmp;
+        try
+        {
+            tmp = File.createTempFile("LogManMBeanTemp", ".tmp");
+            tmp.deleteOnExit();
+            StreamResult result = new StreamResult(tmp);
+            transformer.transform(source, result);
+        }
+        catch (TransformerException e)
+        {
+            _logger.warn("Could not transform the XML into new file: " +e);
+            return false;
+        }
+        catch (IOException e)
+        {
+            _logger.warn("Could not create the new file: " +e);
+            return false;
+        }
+
+        // Swap temp file in to replace existing configuration file.
+        File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
+        if (old.exists())
+        {
+            old.delete();
+        }
+        log4jConfigFile.renameTo(old);
+        return tmp.renameTo(log4jConfigFile);
+    }
+
+
+    /* The log4j XML configuration file DTD defines three possible element
+     * combinations for specifying optional logger+level settings.
+     * Must account for the following:
+     * 
+     * <category name="x"> <priority value="y"/> </category>    OR
+     * <category name="x"> <level value="y"/> </category>    OR
+     * <logger name="x"> <level value="y"/> </logger>
+     *
+     * Noting also that the level/priority child element is optional too,
+     * and not the only possible child element.
+     */
+    
+    
+    public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+    {
+        if (_loggerLevelTabularType == null)
+        {
+            _logger.warn("TabluarData type not set up correctly");
+            return null;
+        }
+        
+        _logger.info("Getting logger levels from log4j configuration file");
+        
+        Document doc = parseConfigFile(_log4jConfigFileName);
+
+        TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+
+        //retrieve the 'category' element nodes
+        NodeList categoryElements = doc.getElementsByTagName("category");
+        
+        String categoryName;
+        String priority = null;
+        
+        for (int i = 0; i < categoryElements.getLength(); i++)
+        {
+            Element categoryElement = (Element) categoryElements.item(i);
+            categoryName = categoryElement.getAttribute("name");
+
+            //retrieve the category's mandatory 'priority' or 'level' element's value.
+            //It may not be the only child node, so request by tag name.
+            NodeList priorityElements = categoryElement.getElementsByTagName("priority");
+            NodeList levelElements = categoryElement.getElementsByTagName("level");
+
+            if (priorityElements.getLength() != 0)
+            {
+                Element priorityElement = (Element) priorityElements.item(0);
+                priority = priorityElement.getAttribute("value").toUpperCase();
+            }
+            else if (levelElements.getLength() != 0)
+            {
+                Element levelElement = (Element) levelElements.item(0);
+                priority = levelElement.getAttribute("value").toUpperCase();
+            }
+            else
+            {
+                //there is no exiting priority or level to view, move onto next category/logger
+                continue;
+            }
+
+            try
+            {
+                Object[] itemData = {categoryName, priority};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+            catch (OpenDataException e)
+            {
+                _logger.warn("Unable to create logger level list due to :" + e);
+                return null;
+            }
+        }
+
+        //retrieve the 'logger' element nodes
+        NodeList loggerElements = doc.getElementsByTagName("logger");
+        
+        String loggerName;
+        String level;
+
+        for (int i = 0; i < loggerElements.getLength(); i++)
+        {
+            Element loggerElement = (Element) loggerElements.item(i);
+            loggerName = loggerElement.getAttribute("name");
+
+            //retrieve the logger's mandatory 'level' element's value
+            //It may not be the only child node, so request by tag name.
+            NodeList levelElements = loggerElement.getElementsByTagName("level");
+
+            Element levelElement = (Element) levelElements.item(0);
+            level = levelElement.getAttribute("value").toUpperCase();
+            
+            try
+            {
+                Object[] itemData = {loggerName, level};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+            catch (OpenDataException e)
+            {
+                _logger.warn("Unable to create logger level list due to :" + e);
+                return null;
+            }
+        }
+        
+        return loggerLevelList;
+    }
+
+    public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException
+    {
+        //check that the specified level is a valid log4j Level
+        try
+        {
+            getLevel(level);
+        }
+        catch (Exception e)
+        {
+            //it isnt a valid level
+            return false;
+        }
+        
+        _logger.info("Setting level to " + level + " for logger '" + logger
+                + "' in log4j xml configuration file: " + _log4jConfigFileName);
+        
+        Document doc = parseConfigFile(_log4jConfigFileName);
+
+        //retrieve the 'category' and 'logger' element nodes
+        NodeList categoryElements = doc.getElementsByTagName("category");
+        NodeList loggerElements = doc.getElementsByTagName("logger");
+        
+        //collect them into a single elements list
+        List<Element> logElements = new ArrayList<Element>();
+        
+        for (int i = 0; i < categoryElements.getLength(); i++)
+        {
+            logElements.add((Element) categoryElements.item(i));
+        }
+        for (int i = 0; i < loggerElements.getLength(); i++)
+        {
+            logElements.add((Element) loggerElements.item(i));
+        }
+
+        //try to locate the specified logger/category in the elements retrieved
+        Element logElement = null;
+        for (Element e : logElements)
+        {
+            if (e.getAttribute("name").equals(logger))
+            {
+                logElement = e;
+                break;
+            }
+        }
+
+        if (logElement == null)
+        {
+            //no loggers/categories with given name found, does not exist to update
+            _logger.warn("Specified logger does not exist in the configuration file: " +logger);
+            return false;
+        }
+
+        //retrieve the optional 'priority' or 'level' sub-element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = logElement.getElementsByTagName("priority");
+        NodeList levelElements = logElement.getElementsByTagName("level");
+
+        Element levelElement = null;
+        if (priorityElements.getLength() != 0)
+        {
+            levelElement = (Element) priorityElements.item(0);
+        }
+        else if (levelElements.getLength() != 0)
+        {
+            levelElement = (Element) levelElements.item(0);
+        }
+        else
+        {
+            //there is no exiting priority or level element to update
+            return false;
+        }
+        
+        //update the element with the new level/priority
+        levelElement.setAttribute("value", level);
+        
+        //output the new file
+        return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+    }
+
+    
+    /* The log4j XML configuration file DTD defines 2 possible element
+     * combinations for specifying the optional root logger level settings
+     * Must account for the following:
+     * 
+     * <root> <priority value="y"/> </root>    OR
+     * <root> <level value="y"/> </root> 
+     *
+     * Noting also that the level/priority child element is optional too,
+     * and not the only possible child element.
+     */
+    
+    public synchronized String getConfigFileRootLoggerLevel() throws IOException
+    {
+        _logger.info("Getting root logger level from log4j configuration file");
+
+        Document doc = parseConfigFile(_log4jConfigFileName);
+       
+        //retrieve the optional 'root' element node
+        NodeList rootElements = doc.getElementsByTagName("root");
+
+        if (rootElements.getLength() == 0)
+        {
+            //there is not root logger definition
+            return null;
+        }
+
+        Element rootElement = (Element) rootElements.item(0);
+
+        //retrieve the optional 'priority' or 'level' element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = rootElement.getElementsByTagName("priority");
+        NodeList levelElements = rootElement.getElementsByTagName("level");
+        String priority = null;
+        
+        if (priorityElements.getLength() != 0)
+        {
+            Element priorityElement = (Element) priorityElements.item(0);
+            priority = priorityElement.getAttribute("value");
+        }
+        else if(levelElements.getLength() != 0)
+        {
+            Element levelElement = (Element) levelElements.item(0);
+            priority = levelElement.getAttribute("value");
+        }
+
+        if(priority != null)
+        {
+            return priority.toUpperCase();
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException
+    {
+        //check that the specified level is a valid log4j Level
+        try
+        {
+            getLevel(level);
+        }
+        catch (Exception e)
+        {
+            //it isnt a valid level
+            return false;
+        }
+        
+        _logger.info("Setting level to " + level + " for the Root logger in " +
+        		"log4j xml configuration file: " + _log4jConfigFileName);
+
+        Document doc = parseConfigFile(_log4jConfigFileName);
+        
+        //retrieve the optional 'root' element node
+        NodeList rootElements = doc.getElementsByTagName("root");
+
+        if (rootElements.getLength() == 0)
+        {
+            return false;
+        }
+
+        Element rootElement = (Element) rootElements.item(0);
+
+        //retrieve the optional 'priority' or 'level' sub-element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = rootElement.getElementsByTagName("priority");
+        NodeList levelElements = rootElement.getElementsByTagName("level");
+
+        Element levelElement = null;
+        if (priorityElements.getLength() != 0)
+        {
+            levelElement = (Element) priorityElements.item(0);
+        }
+        else if (levelElements.getLength() != 0)
+        {
+            levelElement = (Element) levelElements.item(0);
+        }
+        else
+        {
+            //there is no exiting priority/level to update
+            return false;
+        }
+        
+        //update the element with the new level/priority
+        levelElement.setAttribute("value", level);
+
+        //output the new file
+        return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+    }
+}
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.    
+ *
+ * 
+ */
+package org.apache.qpid.server.logging.management;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.qpid.server.management.MBeanDescription;
+import org.apache.qpid.server.management.AMQManagedObject;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.Log4jEntityResolver;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.management.JMException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+
+/** MBean class for BrokerLoggingManagerMBean. It implements all the management features exposed for managing logging. */
+@MBeanDescription("Logging Management Interface")
+public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement
+{
+
+    private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class);
+    private String _log4jConfigFileName;
+    private int _log4jLogWatchInterval;
+    
+    static TabularType _loggerLevelTabularType;
+    static CompositeType _loggerLevelCompositeType;
+
+    static
+    {
+        try
+        {
+            OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING};
+
+            _loggerLevelCompositeType = new CompositeType("LoggerLevelList", "Logger Level Data", 
+                                                         COMPOSITE_ITEM_NAMES, COMPOSITE_ITEM_DESCRIPTIONS, loggerLevelItemTypes);
+
+            _loggerLevelTabularType = new TabularType("LoggerLevel", "List of loggers with levels",
+                                                       _loggerLevelCompositeType, TABULAR_UNIQUE_INDEX);
+        }
+        catch (OpenDataException e)
+        {
+            _logger.error("Tabular data setup for viewing logger levels was incorrect.");
+            _loggerLevelTabularType = null;
+        }
+    }
+    
+    public LoggingManagementMBean(String log4jConfigFileName, int log4jLogWatchInterval) throws JMException
+    {
+        super(LoggingManagement.class, LoggingManagement.TYPE, LoggingManagement.VERSION);
+        _log4jConfigFileName = log4jConfigFileName;
+        _log4jLogWatchInterval = log4jLogWatchInterval;
+    }
+
+    public String getObjectInstanceName()
+    {
+        return LoggingManagement.TYPE;
+    }
+    
+    public Integer getLog4jLogWatchInterval()
+    {
+        return _log4jLogWatchInterval;
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized boolean setRuntimeLoggerLevel(String logger, String level)
+    {   
+        //check specified level is valid
+        Level newLevel;
+        try
+        {
+            newLevel = getLevel(level);
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+        
+        //check specified logger exists
+        Enumeration loggers = LogManager.getCurrentLoggers();
+        Boolean loggerExists = false;
+        
+        while(loggers.hasMoreElements())
+        {
+            Logger log = (Logger) loggers.nextElement();
+            if (log.getName().equals(logger))
+            {
+                loggerExists = true;
+                break;
+            }
+        }
+        
+        if(!loggerExists)
+        {
+            return false;
+        }
+        
+        //set the logger to the new level
+        _logger.info("Setting level to " + level + " for logger: " + logger);
+        
+        Logger log = Logger.getLogger(logger);
+        log.setLevel(newLevel);
+        
+        return true;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public synchronized TabularData viewEffectiveRuntimeLoggerLevels()
+    {
+        if (_loggerLevelTabularType == null)
+        {
+            _logger.warn("TabluarData type not set up correctly");
+            return null;
+        }
+
+        _logger.info("Getting levels for currently active log4j loggers");
+        
+        Enumeration loggers = LogManager.getCurrentLoggers();
+
+        TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+
+        Logger logger;
+        String loggerName;
+        String level;
+        
+        try
+        {
+            while(loggers.hasMoreElements()){
+                logger = (Logger) loggers.nextElement();
+
+                loggerName = logger.getName();
+                level = logger.getEffectiveLevel().toString();
+
+                Object[] itemData = {loggerName, level};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+        }
+        catch (OpenDataException e)
+        {
+            _logger.warn("Unable to create logger level list due to :" + e);
+            return null;
+        }
+
+        return loggerLevelList;
+        
+    }
+    
+    public synchronized String getRuntimeRootLoggerLevel()
+    {
+        Logger rootLogger = Logger.getRootLogger();
+
+        return rootLogger.getLevel().toString();
+    }
+
+    public synchronized boolean setRuntimeRootLoggerLevel(String level)
+    {
+            Level newLevel;
+            try
+            {
+                newLevel = getLevel(level);
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+            
+            _logger.info("Setting RootLogger level to " + level);
+            
+            Logger log = Logger.getRootLogger();
+            log.setLevel(newLevel);
+            
+            return true;
+    }
+    
+    //method to convert from a string to a log4j Level, throws exception if the given value is invalid
+    private Level getLevel(String level) throws Exception
+    {
+        Level newLevel = Level.toLevel(level);
+        
+        //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
+        if (newLevel.equals(Level.DEBUG) && !(level.equalsIgnoreCase("debug")))
+        {
+            //received DEBUG but we did not ask for it, the Level request failed.
+            throw new Exception("Invalid level name");
+        }
+        
+        return newLevel;
+    }
+    
+    //handler to catch errors signalled by the JAXP parser and throw an appropriate exception
+    private class SaxErrorHandler implements ErrorHandler
+    {
+        
+        public void error(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Error parsing XML file: " + e.getMessage());
+        }
+
+        public void fatalError(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Fatal error parsing XML file: " + e.getMessage());
+        }
+
+        public void warning(SAXParseException e) throws SAXException
+        {
+            throw new SAXException("Warning parsing XML file: " + e.getMessage());
+        }
+    }
+
+    //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
+    private synchronized Document parseConfigFile(String fileName) throws IOException
+    {
+        //check file was specified, exists, and is readable
+        if(fileName == null)
+        {
+            _logger.warn("No log4j XML configuration file has been set");
+            throw new IOException("No log4j XML configuration file has been set");
+        }
+
+        File configFile = new File(fileName);
+
+        if (!configFile.exists())
+        {
+            _logger.warn("Specified log4j XML configuration file does not exist: " + fileName);
+            throw new IOException("Specified log4j XML configuration file does not exist");
+        }
+        else if (!configFile.canRead())
+        {
+            _logger.warn("Specified log4j XML configuration file is not readable: " + fileName);
+            throw new IOException("Specified log4j XML configuration file is not readable");
+        }
+
+        //parse it
+        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder docBuilder;
+        Document doc;
+
+        ErrorHandler errHandler = new SaxErrorHandler();
+        try
+        {
+            docFactory.setValidating(true);
+            docBuilder = docFactory.newDocumentBuilder();
+            docBuilder.setErrorHandler(errHandler);
+            docBuilder.setEntityResolver(new Log4jEntityResolver());
+            doc = docBuilder.parse(fileName);
+        }
+        catch (ParserConfigurationException e)
+        {
+            _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e);
+            //recommended that MBeans should use java.* and javax.* exceptions only
+            throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage());
+        }
+        catch (SAXException e)
+        {
+            _logger.warn("The specified log4j XML file is invalid: " + e);
+            //recommended that MBeans should use standard java.* and javax.* exceptions only
+            throw new IOException("The specified log4j XML file is invalid: " + e.getMessage());
+        }
+        catch (IOException e)
+        {
+            _logger.warn("Unable to parse the specified log4j XML file" + e);
+            throw new IOException("Unable to parse the specified log4j XML file", e);
+        }
+
+        return doc;
+    }
+
+    
+    private synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
+    {
+        File log4jConfigFile = new File(log4jConfigFileName);
+        
+        if (!log4jConfigFile.canWrite())
+        {
+            _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
+            throw new IOException("Specified log4j XML configuration file is not writable");
+        }
+
+        Transformer transformer = null;
+        try
+        {
+            transformer = TransformerFactory.newInstance().newTransformer();
+        }
+        catch (Exception e)
+        {
+            _logger.warn("Could not create an XML transformer: " +e);
+            return false;
+        }
+
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
+        DOMSource source = new DOMSource(doc);
+        
+        File tmp;
+        try
+        {
+            tmp = File.createTempFile("LogManMBeanTemp", ".tmp");
+            tmp.deleteOnExit();
+            StreamResult result = new StreamResult(tmp);
+            transformer.transform(source, result);
+        }
+        catch (TransformerException e)
+        {
+            _logger.warn("Could not transform the XML into new file: " +e);
+            return false;
+        }
+        catch (IOException e)
+        {
+            _logger.warn("Could not create the new file: " +e);
+            return false;
+        }
+
+        // Swap temp file in to replace existing configuration file.
+        File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
+        if (old.exists())
+        {
+            old.delete();
+        }
+        log4jConfigFile.renameTo(old);
+        return tmp.renameTo(log4jConfigFile);
+    }
+
+
+    /* The log4j XML configuration file DTD defines three possible element
+     * combinations for specifying optional logger+level settings.
+     * Must account for the following:
+     * 
+     * <category name="x"> <priority value="y"/> </category>    OR
+     * <category name="x"> <level value="y"/> </category>    OR
+     * <logger name="x"> <level value="y"/> </logger>
+     *
+     * Noting also that the level/priority child element is optional too,
+     * and not the only possible child element.
+     */
+    
+    
+    public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+    {
+        if (_loggerLevelTabularType == null)
+        {
+            _logger.warn("TabluarData type not set up correctly");
+            return null;
+        }
+        
+        _logger.info("Getting logger levels from log4j configuration file");
+        
+        Document doc = parseConfigFile(_log4jConfigFileName);
+
+        TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+
+        //retrieve the 'category' element nodes
+        NodeList categoryElements = doc.getElementsByTagName("category");
+        
+        String categoryName;
+        String priority = null;
+        
+        for (int i = 0; i < categoryElements.getLength(); i++)
+        {
+            Element categoryElement = (Element) categoryElements.item(i);
+            categoryName = categoryElement.getAttribute("name");
+
+            //retrieve the category's mandatory 'priority' or 'level' element's value.
+            //It may not be the only child node, so request by tag name.
+            NodeList priorityElements = categoryElement.getElementsByTagName("priority");
+            NodeList levelElements = categoryElement.getElementsByTagName("level");
+
+            if (priorityElements.getLength() != 0)
+            {
+                Element priorityElement = (Element) priorityElements.item(0);
+                priority = priorityElement.getAttribute("value").toUpperCase();
+            }
+            else if (levelElements.getLength() != 0)
+            {
+                Element levelElement = (Element) levelElements.item(0);
+                priority = levelElement.getAttribute("value").toUpperCase();
+            }
+            else
+            {
+                //there is no exiting priority or level to view, move onto next category/logger
+                continue;
+            }
+
+            try
+            {
+                Object[] itemData = {categoryName, priority};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+            catch (OpenDataException e)
+            {
+                _logger.warn("Unable to create logger level list due to :" + e);
+                return null;
+            }
+        }
+
+        //retrieve the 'logger' element nodes
+        NodeList loggerElements = doc.getElementsByTagName("logger");
+        
+        String loggerName;
+        String level;
+
+        for (int i = 0; i < loggerElements.getLength(); i++)
+        {
+            Element loggerElement = (Element) loggerElements.item(i);
+            loggerName = loggerElement.getAttribute("name");
+
+            //retrieve the logger's mandatory 'level' element's value
+            //It may not be the only child node, so request by tag name.
+            NodeList levelElements = loggerElement.getElementsByTagName("level");
+
+            Element levelElement = (Element) levelElements.item(0);
+            level = levelElement.getAttribute("value").toUpperCase();
+            
+            try
+            {
+                Object[] itemData = {loggerName, level};
+                CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType, COMPOSITE_ITEM_NAMES, itemData);
+                loggerLevelList.put(loggerData);
+            }
+            catch (OpenDataException e)
+            {
+                _logger.warn("Unable to create logger level list due to :" + e);
+                return null;
+            }
+        }
+        
+        return loggerLevelList;
+    }
+
+    public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException
+    {
+        //check that the specified level is a valid log4j Level
+        try
+        {
+            getLevel(level);
+        }
+        catch (Exception e)
+        {
+            //it isnt a valid level
+            return false;
+        }
+        
+        _logger.info("Setting level to " + level + " for logger '" + logger
+                + "' in log4j xml configuration file: " + _log4jConfigFileName);
+        
+        Document doc = parseConfigFile(_log4jConfigFileName);
+
+        //retrieve the 'category' and 'logger' element nodes
+        NodeList categoryElements = doc.getElementsByTagName("category");
+        NodeList loggerElements = doc.getElementsByTagName("logger");
+        
+        //collect them into a single elements list
+        List<Element> logElements = new ArrayList<Element>();
+        
+        for (int i = 0; i < categoryElements.getLength(); i++)
+        {
+            logElements.add((Element) categoryElements.item(i));
+        }
+        for (int i = 0; i < loggerElements.getLength(); i++)
+        {
+            logElements.add((Element) loggerElements.item(i));
+        }
+
+        //try to locate the specified logger/category in the elements retrieved
+        Element logElement = null;
+        for (Element e : logElements)
+        {
+            if (e.getAttribute("name").equals(logger))
+            {
+                logElement = e;
+                break;
+            }
+        }
+
+        if (logElement == null)
+        {
+            //no loggers/categories with given name found, does not exist to update
+            _logger.warn("Specified logger does not exist in the configuration file: " +logger);
+            return false;
+        }
+
+        //retrieve the optional 'priority' or 'level' sub-element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = logElement.getElementsByTagName("priority");
+        NodeList levelElements = logElement.getElementsByTagName("level");
+
+        Element levelElement = null;
+        if (priorityElements.getLength() != 0)
+        {
+            levelElement = (Element) priorityElements.item(0);
+        }
+        else if (levelElements.getLength() != 0)
+        {
+            levelElement = (Element) levelElements.item(0);
+        }
+        else
+        {
+            //there is no exiting priority or level element to update
+            return false;
+        }
+        
+        //update the element with the new level/priority
+        levelElement.setAttribute("value", level);
+        
+        //output the new file
+        return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+    }
+
+    
+    /* The log4j XML configuration file DTD defines 2 possible element
+     * combinations for specifying the optional root logger level settings
+     * Must account for the following:
+     * 
+     * <root> <priority value="y"/> </root>    OR
+     * <root> <level value="y"/> </root> 
+     *
+     * Noting also that the level/priority child element is optional too,
+     * and not the only possible child element.
+     */
+    
+    public synchronized String getConfigFileRootLoggerLevel() throws IOException
+    {
+        _logger.info("Getting root logger level from log4j configuration file");
+
+        Document doc = parseConfigFile(_log4jConfigFileName);
+       
+        //retrieve the optional 'root' element node
+        NodeList rootElements = doc.getElementsByTagName("root");
+
+        if (rootElements.getLength() == 0)
+        {
+            //there is not root logger definition
+            return null;
+        }
+
+        Element rootElement = (Element) rootElements.item(0);
+
+        //retrieve the optional 'priority' or 'level' element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = rootElement.getElementsByTagName("priority");
+        NodeList levelElements = rootElement.getElementsByTagName("level");
+        String priority = null;
+        
+        if (priorityElements.getLength() != 0)
+        {
+            Element priorityElement = (Element) priorityElements.item(0);
+            priority = priorityElement.getAttribute("value");
+        }
+        else if(levelElements.getLength() != 0)
+        {
+            Element levelElement = (Element) levelElements.item(0);
+            priority = levelElement.getAttribute("value");
+        }
+
+        if(priority != null)
+        {
+            return priority.toUpperCase();
+        }
+        else
+        {
+            return null;
+        }
+    }
+    
+    public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException
+    {
+        //check that the specified level is a valid log4j Level
+        try
+        {
+            getLevel(level);
+        }
+        catch (Exception e)
+        {
+            //it isnt a valid level
+            return false;
+        }
+        
+        _logger.info("Setting level to " + level + " for the Root logger in " +
+        		"log4j xml configuration file: " + _log4jConfigFileName);
+
+        Document doc = parseConfigFile(_log4jConfigFileName);
+        
+        //retrieve the optional 'root' element node
+        NodeList rootElements = doc.getElementsByTagName("root");
+
+        if (rootElements.getLength() == 0)
+        {
+            return false;
+        }
+
+        Element rootElement = (Element) rootElements.item(0);
+
+        //retrieve the optional 'priority' or 'level' sub-element value.
+        //It may not be the only child node, so request by tag name.
+        NodeList priorityElements = rootElement.getElementsByTagName("priority");
+        NodeList levelElements = rootElement.getElementsByTagName("level");
+
+        Element levelElement = null;
+        if (priorityElements.getLength() != 0)
+        {
+            levelElement = (Element) priorityElements.item(0);
+        }
+        else if (levelElements.getLength() != 0)
+        {
+            levelElement = (Element) levelElements.item(0);
+        }
+        else
+        {
+            //there is no exiting priority/level to update
+            return false;
+        }
+        
+        //update the element with the new level/priority
+        levelElement.setAttribute("value", level);
+
+        //output the new file
+        return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+    }
+}

Modified: qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java?rev=747870&r1=747869&r2=747870&view=diff
==============================================================================
--- qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java (original)
+++ qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java Wed Feb 25 17:53:09 2009
@@ -20,6 +20,7 @@
  */
 package org.apache.qpid.server.management;
 
+import org.apache.qpid.server.logging.management.LoggingManagement;
 import org.apache.qpid.server.security.access.management.UserManagement;
 import org.apache.log4j.Logger;
 
@@ -153,7 +154,12 @@
         if (args[0] instanceof ObjectName)
         {
             ObjectName object = (ObjectName) args[0];
-            return UserManagement.TYPE.equals(object.getKeyProperty("type"));
+            
+            if (UserManagement.TYPE.equals(object.getKeyProperty("type"))
+                    || LoggingManagement.TYPE.equals(object.getKeyProperty("type")))
+            {
+                return true;
+            }
         }
 
         return false;



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org


Mime
View raw message