summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Gemmell <robbie@apache.org>2009-08-16 20:32:47 +0000
committerRobert Gemmell <robbie@apache.org>2009-08-16 20:32:47 +0000
commitb6dc0084d1ba75116fe832adaea231f2ba71aa1a (patch)
tree1e2259fa581f0157c02a9c2280c8634e722f9b28
parent1a8e9123f10f7fac4f8ceb1b6f2bf85b980ce45e (diff)
downloadqpid-python-b6dc0084d1ba75116fe832adaea231f2ba71aa1a.tar.gz
QPID-2051: Update startup scripts to disable the Log4J default initialisation process. Add QpidLog4JConfigurator that validates the XML file before allowing it to be applied. Alter startup behaviour to shut the broker down if the specified log4j XML file is present present but invalid. Uses the -Damqj.logging.level(defaults to info) with the log4j.properties file in the broker jar if the XML file is not found.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@804765 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xqpid/java/broker/bin/qpid-server8
-rw-r--r--[-rwxr-xr-x]qpid/java/broker/bin/qpid-server.bat5
-rw-r--r--qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java250
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java43
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java104
-rw-r--r--qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java396
6 files changed, 752 insertions, 54 deletions
diff --git a/qpid/java/broker/bin/qpid-server b/qpid/java/broker/bin/qpid-server
index e5a9e998e2..7482ad63ef 100755
--- a/qpid/java/broker/bin/qpid-server
+++ b/qpid/java/broker/bin/qpid-server
@@ -23,6 +23,12 @@ if [ -z "$QPID_HOME" ]; then
export PATH=${PATH}:${QPID_HOME}/bin
fi
+if [ -z "$QPID_LOG4J_SETTINGS" ]; then
+ # Disable the Log4J default initialization process, allowing the broker to
+ # perform the configuration using etc/log4j.xml
+ QPID_LOG4J_SETTINGS="-Dlog4j.defaultInitOverride=true"
+fi
+
# Set classpath to include Qpid jar with all required jars in manifest
QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar
@@ -34,6 +40,6 @@ export JAVA=java \
QPID_CLASSPATH=$QPID_LIBS \
QPID_RUN_LOG=2
-QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32"
+QPID_OPTS="$QPID_OPTS $QPID_LOG4J_SETTINGS -Damqj.read_write_pool_size=32"
. qpid-run org.apache.qpid.server.Main "$@"
diff --git a/qpid/java/broker/bin/qpid-server.bat b/qpid/java/broker/bin/qpid-server.bat
index 2687baa111..7fff183192 100755..100644
--- a/qpid/java/broker/bin/qpid-server.bat
+++ b/qpid/java/broker/bin/qpid-server.bat
@@ -20,6 +20,9 @@
@echo off
REM Script to run the Qpid Java Broker
+rem stop the Log4J default initialisation, let the broker do it using etc/log4j.xml
+if "%QPID_LOG4J_SETTINGS%" == "" set QPID_LOG4J_SETTINGS=-Dlog4j.defaultInitOverride=true
+
rem Guess QPID_HOME if not defined
set CURRENT_DIR=%cd%
if not "%QPID_HOME%" == "" goto gotHome
@@ -194,7 +197,7 @@ rem QPID_OPTS intended to hold any -D props for use
rem user must enclose any value for QPID_OPTS in double quotes
:runCommand
set MODULE_JARS=%QPID_MODULE_JARS%
-set COMMAND="%JAVA_HOME%\bin\java" %JAVA_VM% %JAVA_MEM% %JAVA_GC% %QPID_OPTS% %SYSTEM_PROPS% -cp "%CLASSPATH%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS%
+set COMMAND="%JAVA_HOME%\bin\java" %JAVA_VM% %JAVA_MEM% %JAVA_GC% %QPID_OPTS% %QPID_LOG4J_SETTINGS% %SYSTEM_PROPS% -cp "%CLASSPATH%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS%
if "%debug%" == "true" echo %CLASSPATH%;%LAUNCH_JAR%;%MODULE_JARS%
if "%debug%" == "true" echo %COMMAND%
diff --git a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
new file mode 100644
index 0000000000..53134250ab
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
@@ -0,0 +1,250 @@
+/*
+ *
+ * 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.log4j.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.logging.management.LoggingManagementMBean;
+import org.apache.qpid.server.logging.management.LoggingManagementMBean.QpidLog4JSaxErrorHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Substitute for the Log4J XMLWatchdog (as used by DOMConfigurator.configureAndWatch)
+ *
+ * Extends the default behaviour with a strict parser check on the XML file before allowing the reconfiguration to proceed,
+ * ensuring that any parser error or warning prevents initiation of a configuration update by Log4J, which aborts mid-update
+ * upon fatal errors from the parser and proceeds in the event of 'regular' parser errors and warnings, in all cases allowing
+ * startup to proceed with whatever half-baked configuration then exists.
+ */
+public class QpidLog4JConfigurator
+{
+ //lock to protect access to the configuration file
+ private static final ReentrantLock LOCK = new ReentrantLock();
+ private static Logger _logger;
+
+ private QpidLog4JConfigurator()
+ {
+ //no instances
+ }
+
+ public static void configure(String filename) throws IOException, ParserConfigurationException,
+ SAXException, IllegalLoggerLevelException
+ {
+ try
+ {
+ LOCK.lock();
+
+ strictlyParseXMLConfigFile(filename);
+ checkLoggerLevels(filename);
+
+ DOMConfigurator.configure(filename);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ public static void configureAndWatch(String filename, long delay) throws IOException, ParserConfigurationException,
+ SAXException, IllegalLoggerLevelException
+ {
+ strictlyParseXMLConfigFile(filename);
+ checkLoggerLevels(filename);
+
+ QpidLog4JXMLWatchdog watchdog = new QpidLog4JXMLWatchdog(filename);
+ watchdog.setDelay(delay);
+ watchdog.start();
+ }
+
+ private static void strictlyParseXMLConfigFile(String fileName) throws IOException, SAXException,
+ ParserConfigurationException
+ {
+ try
+ {
+ LOCK.lock();
+
+ //check file was specified, exists, and is readable
+ if(fileName == null)
+ {
+ throw new IOException("Provided log4j XML configuration filename was null");
+ }
+
+ File configFile = new File(fileName);
+
+ if (!configFile.exists())
+ {
+ throw new IOException("The log4j XML configuration file does not exist: " + fileName);
+ }
+ else if (!configFile.canRead())
+ {
+ throw new IOException("The log4j XML configuration file is not readable: " + fileName);
+ }
+
+ //parse it
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder;
+
+ ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
+
+ docFactory.setValidating(true);
+ docBuilder = docFactory.newDocumentBuilder();
+ docBuilder.setErrorHandler(errHandler);
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+ docBuilder.parse(fileName);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ private static class QpidLog4JXMLWatchdog extends XMLWatchdog
+ {
+ public QpidLog4JXMLWatchdog(String filename)
+ {
+ super(filename);
+ }
+
+ public void doOnChange()
+ {
+ try
+ {
+ LOCK.lock();
+
+ try
+ {
+ strictlyParseXMLConfigFile(filename);
+ }
+ catch (Exception e)
+ {
+ //logger will be instantiated following first configuration success, which has been pre-validated
+ //and so the null check should never actually be required.
+ if(_logger != null)
+ {
+ _logger.warn("Parsing the log4j XML configuration file generated errors/warnings. " +
+ "The new configuration was not applied. Correct the issues to prompt " +
+ "another update attempt: " + e.getMessage());
+ }
+ return;
+ }
+
+ try
+ {
+ checkLoggerLevels(filename);
+ }
+ catch (Exception e)
+ {
+ //logger will be instantiated following first configuration success, which has been pre-validated
+ //and so the null check should never actually be required.
+ if(_logger != null)
+ {
+ _logger.warn("Errors were found when validating the logger level values in the " +
+ "log4j XML configuration file. The new configuration was not applied. " +
+ "Correct the issues to prompt another update attempt: " + e.getMessage());
+ }
+ return;
+ }
+
+ //everything checked was ok, let the normal update process proceed
+ super.doOnChange();
+
+ //a configuration has now been applied, enable logging for future attempts
+ if(_logger == null)
+ {
+ _logger = Logger.getLogger(QpidLog4JConfigurator.class);
+ }
+
+ _logger.info("Applied log4j configuration from: " + filename);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+
+ }
+ }
+
+ protected static void checkLoggerLevels(String filename) throws IllegalLoggerLevelException, IOException
+ {
+ //check that the logger levels specified in the XML are actually valid
+
+ try
+ {
+ LOCK.lock();
+
+ Map<String, String> loggersLevels;
+ loggersLevels = LoggingManagementMBean.retrieveConfigFileLoggersLevels(filename);
+
+ for (String loggerName : loggersLevels.keySet())
+ {
+ String levelString = loggersLevels.get(loggerName);
+ checkLevel(loggerName,levelString);
+ }
+
+ //check the root logger level
+ String rootLoggerlevelString = LoggingManagementMBean.retrieveConfigFileRootLoggerLevel(filename);
+ checkLevel("Root", rootLoggerlevelString);
+ }
+ finally
+ {
+ LOCK.unlock();
+ }
+ }
+
+ private static void checkLevel(String loggerName, String levelString) throws IllegalLoggerLevelException
+ {
+ if("null".equalsIgnoreCase(levelString) || "inherited".equalsIgnoreCase(levelString))
+ {
+ //the string "null" signals to inherit from a parent logger
+ return;
+ }
+
+ Level level = Level.toLevel(levelString);
+
+ //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
+ if (level.equals(Level.DEBUG) && !(levelString.equalsIgnoreCase("debug")))
+ {
+ //received DEBUG but we did not ask for it, the Level request failed.
+ throw new IllegalLoggerLevelException("Level '" + levelString + "' specified for Logger '" + loggerName + "' is invalid");
+ }
+ }
+
+ public static class IllegalLoggerLevelException extends Exception
+ {
+ private static final long serialVersionUID = 1L;
+
+ public IllegalLoggerLevelException(String msg)
+ {
+ super(msg);
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
index eac027afc6..8566ba6270 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java
@@ -27,9 +27,9 @@ import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
-import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
-import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.xml.QpidLog4JConfigurator;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.FixedSizeByteBufferAllocator;
import org.apache.mina.common.IoAcceptor;
@@ -56,9 +56,11 @@ import org.apache.qpid.server.transport.QpidAcceptor;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.util.Properties;
/**
* Main entry point for AMQPD.
@@ -200,7 +202,7 @@ public class Main
}
catch (InitException e)
{
- System.out.println(e.getMessage());
+ System.out.println("Initialisation Error : " + e.getMessage());
_brokerLogger.error("Initialisation Error : " + e.getMessage());
shutdown(1);
}
@@ -498,7 +500,7 @@ public class Main
return ip;
}
- private void configureLogging(File logConfigFile, int logWatchTime)
+ private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException
{
if (logConfigFile.exists() && logConfigFile.canRead())
{
@@ -509,18 +511,43 @@ public class Main
System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every "
+ logWatchTime + " seconds");
// log4j expects the watch interval in milliseconds
- DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000);
+ try
+ {
+ QpidLog4JConfigurator.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000);
+ }
+ catch (Exception e)
+ {
+ throw new InitException(e.getMessage(),e);
+ }
}
else
{
- DOMConfigurator.configure(logConfigFile.getAbsolutePath());
+ try
+ {
+ QpidLog4JConfigurator.configure(logConfigFile.getPath());
+ }
+ catch (Exception e)
+ {
+ throw new InitException(e.getMessage(),e);
+ }
}
}
else
{
System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath());
- System.err.println("Using basic log4j configuration");
- BasicConfigurator.configure();
+ System.err.println("Using the fallback internal log4j.properties configuration");
+
+ InputStream propsFile = this.getClass().getResourceAsStream("/log4j.properties");
+ if(propsFile == null)
+ {
+ throw new IOException("Unable to load the fallback internal log4j.properties configuration file");
+ }
+ else
+ {
+ Properties fallbackProps = new Properties();
+ fallbackProps.load(propsFile);
+ PropertyConfigurator.configure(fallbackProps);
+ }
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
index 3cebb1353b..396e81e690 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/management/LoggingManagementMBean.java
@@ -24,7 +24,9 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.qpid.management.common.mbeans.LoggingManagement;
import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
@@ -34,6 +36,8 @@ import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.Log4jEntityResolver;
+import org.apache.log4j.xml.QpidLog4JConfigurator;
+import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -244,46 +248,50 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
}
//handler to catch errors signalled by the JAXP parser and throw an appropriate exception
- private class SaxErrorHandler implements ErrorHandler
+ public static class QpidLog4JSaxErrorHandler implements ErrorHandler
{
-
public void error(SAXParseException e) throws SAXException
{
- throw new SAXException("Error parsing XML file: " + e.getMessage());
+ throw new SAXException(constructMessage("Error parsing XML file", e));
}
public void fatalError(SAXParseException e) throws SAXException
{
- throw new SAXException("Fatal error parsing XML file: " + e.getMessage());
+ throw new SAXException(constructMessage("Fatal error parsing XML file", e));
}
public void warning(SAXParseException e) throws SAXException
{
- throw new SAXException("Warning parsing XML file: " + e.getMessage());
+ throw new SAXException(constructMessage("Warning parsing XML file", e));
+ }
+
+ private static String constructMessage(final String msg, final SAXParseException ex)
+ {
+ return new String(msg + ": Line " + ex.getLineNumber()+" column " +ex.getColumnNumber() + ": " + ex.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
+ private static 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");
+ _logger.warn("Provided log4j XML configuration filename is null");
+ throw new IOException("Provided log4j XML configuration filename is null");
}
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");
+ _logger.warn("The log4j XML configuration file could not be found: " + fileName);
+ throw new IOException("The log4j XML configuration file could not be found");
}
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");
+ _logger.warn("The log4j XML configuration file is not readable: " + fileName);
+ throw new IOException("The log4j XML configuration file is not readable");
}
//parse it
@@ -291,7 +299,7 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
DocumentBuilder docBuilder;
Document doc;
- ErrorHandler errHandler = new SaxErrorHandler();
+ ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
try
{
docFactory.setValidating(true);
@@ -315,14 +323,14 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
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);
+ throw new IOException("Unable to parse the specified log4j XML file: " + e.getMessage());
}
return doc;
}
- private synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
+ private static synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
{
File log4jConfigFile = new File(log4jConfigFileName);
@@ -389,20 +397,11 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
* and not the only possible child element.
*/
-
- public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+ public static synchronized Map<String,String> retrieveConfigFileLoggersLevels(String fileName) 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);
+ Document doc = parseConfigFile(fileName);
- TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+ HashMap<String,String> loggerLevelList = new HashMap<String,String>();
//retrieve the 'category' element nodes
NodeList categoryElements = doc.getElementsByTagName("category");
@@ -436,17 +435,7 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
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;
- }
+ loggerLevelList.put(categoryName, priority);
}
//retrieve the 'logger' element nodes
@@ -467,6 +456,30 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
Element levelElement = (Element) levelElements.item(0);
level = levelElement.getAttribute("value").toUpperCase();
+ loggerLevelList.put(loggerName, level);
+ }
+
+ return loggerLevelList;
+ }
+
+ 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");
+
+ TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
+
+ Map<String,String> levels = retrieveConfigFileLoggersLevels(_log4jConfigFileName);
+
+ for (String loggerName : levels.keySet())
+ {
+ String level = levels.get(loggerName);
+
try
{
Object[] itemData = {loggerName, level};
@@ -574,19 +587,17 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
* and not the only possible child element.
*/
- public synchronized String getConfigFileRootLoggerLevel() throws IOException
+ public static synchronized String retrieveConfigFileRootLoggerLevel(String fileName) throws IOException
{
- _logger.info("Getting root logger level from log4j configuration file");
-
- Document doc = parseConfigFile(_log4jConfigFileName);
+ Document doc = parseConfigFile(fileName);
//retrieve the optional 'root' element node
NodeList rootElements = doc.getElementsByTagName("root");
if (rootElements.getLength() == 0)
{
- //there is not root logger definition
- return null;
+ //there is no root logger definition
+ return "N/A";
}
Element rootElement = (Element) rootElements.item(0);
@@ -618,6 +629,11 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
}
}
+ public synchronized String getConfigFileRootLoggerLevel() throws IOException
+ {
+ return retrieveConfigFileRootLoggerLevel(_log4jConfigFileName);
+ }
+
public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException
{
//check that the specified level is a valid log4j Level
diff --git a/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
new file mode 100644
index 0000000000..643f1fa48e
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/log4j/xml/QpidLog4JConfiguratorTest.java
@@ -0,0 +1,396 @@
+/*
+ * 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.log4j.xml;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
+
+import junit.framework.TestCase;
+
+public class QpidLog4JConfiguratorTest extends TestCase
+{
+ private static final String NEWLINE = System.getProperty("line.separator");
+
+ private File _testConfigFile;
+
+ private File createTempTestLog4JConfig(String loggerLevel,String rootLoggerLevel, boolean missingTagClose, boolean incorrectAttribute)
+ {
+ File tmpFile = null;
+ try
+ {
+ tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp");
+ tmpFile.deleteOnExit();
+
+ FileWriter fstream = new FileWriter(tmpFile);
+ BufferedWriter writer = new BufferedWriter(fstream);
+
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE);
+ writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE);
+
+ writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " +
+ "threshold=\"null\">"+NEWLINE);
+
+ writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE);
+ writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE);
+ writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE);
+ writer.write(" </layout>"+NEWLINE);
+ writer.write(" </appender>"+NEWLINE);
+
+ String closeTag="/";
+ if(missingTagClose)
+ {
+ closeTag="";
+ }
+
+ //Example of a 'category' with a 'priority'
+ writer.write(" <category additivity=\"true\" name=\"logger1\">"+NEWLINE);
+ writer.write(" <priority value=\"" + loggerLevel+ "\"" + closeTag + ">"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ String attributeName="value";
+ if(incorrectAttribute)
+ {
+ attributeName="values";
+ }
+
+ //Example of a 'category' with a 'level'
+ writer.write(" <category additivity=\"true\" name=\"logger2\">"+NEWLINE);
+ writer.write(" <level " + attributeName + "=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </category>"+NEWLINE);
+
+ //Example of a 'logger' with a 'level'
+ writer.write(" <logger additivity=\"true\" name=\"logger3\">"+NEWLINE);
+ writer.write(" <level value=\"" + loggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </logger>"+NEWLINE);
+
+ //'root' logger
+ writer.write(" <root>"+NEWLINE);
+ writer.write(" <priority value=\"" + rootLoggerLevel+ "\"/>"+NEWLINE);
+ writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
+ writer.write(" </root>"+NEWLINE);
+
+ writer.write("</log4j:configuration>"+NEWLINE);
+
+ writer.flush();
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create temporary test log4j configuration");
+ }
+
+ return tmpFile;
+ }
+
+
+
+ //******* Test Methods ******* //
+
+ public void testCheckLevelsAndStrictParser()
+ {
+ //try the valid logger levels
+ _testConfigFile = createTempTestLog4JConfig("all", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("trace", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("debug", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("warn", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("error", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("fatal", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("off", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("null", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("inherited", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("madeup", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try the valid rootLogger levels
+ _testConfigFile = createTempTestLog4JConfig("info", "all", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "trace", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "warn", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "error", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "fatal", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "off", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "null", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "inherited", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "debug", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ fail("No exception expected, valid levels and xml were used");
+ }
+
+ //now try an invalid logger level
+ _testConfigFile = createTempTestLog4JConfig("info", "madeup", false, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IllegalLoggerLevelException expected, invalid levels used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ //expected, ignore
+ }
+ catch (IOException e)
+ {
+ fail("Incorrect Exception, expected an IllegalLoggerLevelException");
+ }
+
+
+
+ //now try invalid xml
+ _testConfigFile = createTempTestLog4JConfig("info", "info", true, false);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ fail("Incorrect Exception, expected an IOException");
+ }
+ catch (IOException e)
+ {
+ //expected, ignore
+ }
+
+ _testConfigFile = createTempTestLog4JConfig("info", "info", false, true);
+ try
+ {
+ QpidLog4JConfigurator.checkLoggerLevels(_testConfigFile.getAbsolutePath());
+ fail("IOException expected, malformed XML used");
+ }
+ catch (IllegalLoggerLevelException e)
+ {
+ fail("Incorrect Exception, expected an IOException");
+ }
+ catch (IOException e)
+ {
+ //expected, ignore
+ }
+ }
+}