Merge branch 'develop' into feature/JAL-3390hideUnmappedStructure
[jalview.git] / srcjar_unused / org / apache / log4j / xml / DOMConfigurator.java
diff --git a/srcjar_unused/org/apache/log4j/xml/DOMConfigurator.java b/srcjar_unused/org/apache/log4j/xml/DOMConfigurator.java
new file mode 100644 (file)
index 0000000..d3b6644
--- /dev/null
@@ -0,0 +1,1129 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Hashtable;
+import java.util.Properties;
+
+// Contributors:   Mark Womack
+//                 Arun Katkere 
+
+/**
+   Use this class to initialize the log4j environment using a DOM tree.
+
+   <p>The DTD is specified in <a
+   href="doc-files/log4j.dtd"><b>log4j.dtd</b></a>.
+
+   <p>Sometimes it is useful to see how log4j is reading configuration
+   files. You can enable log4j internal logging by defining the
+   <b>log4j.debug</b> variable on the java command
+   line. Alternatively, set the <code>debug</code> attribute in the
+   <code>log4j:configuration</code> element. As in
+<pre>
+   &lt;log4j:configuration <b>debug="true"</b> xmlns:log4j="http://jakarta.apache.org/log4j/">
+   ...
+   &lt;/log4j:configuration>
+</pre>
+
+   <p>There are sample XML files included in the package.
+   
+   @author Christopher Taylor
+   @author Ceki G&uuml;lc&uuml;
+   @author Anders Kristensen
+
+   @since 0.8.3 */
+public class DOMConfigurator implements Configurator {
+
+  static final String CONFIGURATION_TAG = "log4j:configuration";
+  static final String OLD_CONFIGURATION_TAG = "configuration";
+  static final String RENDERER_TAG      = "renderer";
+  private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+  static final String APPENDER_TAG     = "appender";
+  static final String APPENDER_REF_TAG         = "appender-ref";  
+  static final String PARAM_TAG        = "param";
+  static final String LAYOUT_TAG       = "layout";
+  static final String CATEGORY         = "category";
+  static final String LOGGER           = "logger";
+  static final String LOGGER_REF       = "logger-ref";
+  static final String CATEGORY_FACTORY_TAG  = "categoryFactory";
+  static final String LOGGER_FACTORY_TAG  = "loggerFactory";
+  static final String NAME_ATTR                = "name";
+  static final String CLASS_ATTR        = "class";
+  static final String VALUE_ATTR       = "value";
+  static final String ROOT_TAG         = "root";
+  static final String ROOT_REF         = "root-ref";
+  static final String LEVEL_TAG                = "level";
+  static final String PRIORITY_TAG      = "priority";
+  static final String FILTER_TAG       = "filter";
+  static final String ERROR_HANDLER_TAG        = "errorHandler";
+  static final String REF_ATTR         = "ref";
+  static final String ADDITIVITY_ATTR    = "additivity";  
+  static final String THRESHOLD_ATTR       = "threshold";
+  static final String CONFIG_DEBUG_ATTR  = "configDebug";
+  static final String INTERNAL_DEBUG_ATTR  = "debug";
+  private static final String RESET_ATTR  = "reset";
+  static final String RENDERING_CLASS_ATTR = "renderingClass";
+  static final String RENDERED_CLASS_ATTR = "renderedClass";
+
+  static final String EMPTY_STR = "";
+  static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
+
+  final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+
+  
+  // key: appenderName, value: appender
+  Hashtable appenderBag;
+
+  Properties props;
+  LoggerRepository repository;
+
+  protected LoggerFactory catFactory = null;
+
+  /**
+     No argument constructor.
+  */
+  public
+  DOMConfigurator () { 
+    appenderBag = new Hashtable();
+  }
+
+  /**
+     Used internally to parse appenders by IDREF name.
+  */
+  protected
+  Appender findAppenderByName(Document doc, String appenderName)  {      
+    Appender appender = (Appender) appenderBag.get(appenderName);
+
+    if(appender != null) {
+      return appender;
+    } else {
+      // Doesn't work on DOM Level 1 :
+      // Element element = doc.getElementById(appenderName);
+                        
+      // Endre's hack:
+      Element element = null;
+      NodeList list = doc.getElementsByTagName("appender");
+      for (int t=0; t < list.getLength(); t++) {
+       Node node = list.item(t);
+       NamedNodeMap map= node.getAttributes();
+       Node attrNode = map.getNamedItem("name");
+       if (appenderName.equals(attrNode.getNodeValue())) {
+         element = (Element) node;
+         break;
+       }
+      }
+      // Hack finished.
+
+      if(element == null) {
+       LogLog.error("No appender named ["+appenderName+"] could be found."); 
+       return null;
+      } else {
+             appender = parseAppender(element);
+          if (appender != null) {
+            appenderBag.put(appenderName, appender);
+          }
+    return appender;
+      }
+    } 
+  }
+  /**
+     Used internally to parse appenders by IDREF element.
+   */
+  protected
+  Appender findAppenderByReference(Element appenderRef) {    
+    String appenderName = subst(appenderRef.getAttribute(REF_ATTR));    
+    Document doc = appenderRef.getOwnerDocument();
+    return findAppenderByName(doc, appenderName);
+  }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser.
+     * @since 1.2.15
+     * @param instance instance, may be null.
+     * @param element element, may not be null.
+     * @param props properties
+     * @throws IOException thrown if configuration of owner object
+     * should be abandoned.
+     */
+  private static void parseUnrecognizedElement(final Object instance,
+                                        final Element element,
+                                        final Properties props) throws Exception {
+      boolean recognized = false;
+      if (instance instanceof UnrecognizedElementHandler) {
+          recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+                  element, props);
+      }
+      if (!recognized) {
+          LogLog.warn("Unrecognized element " + element.getNodeName());
+      }
+  }
+
+    /**
+      * Delegates unrecognized content to created instance if
+      * it supports UnrecognizedElementParser and catches and
+     *  logs any exception.
+      * @since 1.2.15
+      * @param instance instance, may be null.
+      * @param element element, may not be null.
+      * @param props properties
+      */
+   private static void quietParseUnrecognizedElement(final Object instance,
+                                          final Element element,
+                                          final Properties props) {
+      try {
+          parseUnrecognizedElement(instance, element, props);
+      } catch (Exception ex) {
+          if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+              Thread.currentThread().interrupt();
+          }
+          LogLog.error("Error in extension content: ", ex);
+      }
+  }
+
+  /**
+     Used internally to parse an appender element.
+   */
+  protected
+  Appender parseAppender (Element appenderElement) {
+    String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+    LogLog.debug("Class name: [" + className+']');    
+    try {
+      Object instance  = Loader.loadClass(className).newInstance();
+      Appender appender        = (Appender)instance;
+      PropertySetter propSetter = new PropertySetter(appender);
+
+      appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+      
+      NodeList children        = appenderElement.getChildNodes();
+      final int length         = children.getLength();
+
+      for (int loop = 0; loop < length; loop++) {
+       Node currentNode = children.item(loop);
+
+       /* We're only interested in Elements */
+       if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+         Element currentElement = (Element)currentNode;
+
+         // Parse appender parameters 
+         if (currentElement.getTagName().equals(PARAM_TAG)) {
+            setParameter(currentElement, propSetter);
+         }
+         // Set appender layout
+         else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
+           appender.setLayout(parseLayout(currentElement));
+         }
+         // Add filters
+         else if (currentElement.getTagName().equals(FILTER_TAG)) {
+           parseFilters(currentElement, appender);
+         }
+         else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
+           parseErrorHandler(currentElement, appender);
+         }
+         else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
+           String refName = subst(currentElement.getAttribute(REF_ATTR));
+           if(appender instanceof AppenderAttachable) {
+             AppenderAttachable aa = (AppenderAttachable) appender;
+             LogLog.debug("Attaching appender named ["+ refName+
+                          "] to appender named ["+ appender.getName()+"].");
+             aa.addAppender(findAppenderByReference(currentElement));
+           } else {
+             LogLog.error("Requesting attachment of appender named ["+
+                          refName+ "] to appender named ["+ appender.getName()+
+                "] which does not implement org.apache.log4j.spi.AppenderAttachable.");
+           }
+         } else {
+          parseUnrecognizedElement(instance, currentElement, props);
+      }
+       }
+      }
+      propSetter.activate();
+      return appender;
+    }
+    /* Yes, it's ugly.  But all of these exceptions point to the same
+       problem: we can't create an Appender */
+    catch (Exception oops) {
+        if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+            Thread.currentThread().interrupt();
+        }
+      LogLog.error("Could not create an Appender. Reported error follows.",
+                  oops);
+      return null;
+    }
+  }
+
+  /**
+     Used internally to parse an {@link ErrorHandler} element.
+   */
+  protected
+  void parseErrorHandler(Element element, Appender appender) {
+    ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+                                       subst(element.getAttribute(CLASS_ATTR)),
+                                       org.apache.log4j.spi.ErrorHandler.class, 
+                                      null);
+    
+    if(eh != null) {
+      eh.setAppender(appender);
+
+      PropertySetter propSetter = new PropertySetter(eh);
+      NodeList children = element.getChildNodes();
+      final int length         = children.getLength();
+
+      for (int loop = 0; loop < length; loop++) {
+       Node currentNode = children.item(loop);
+       if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+         Element currentElement = (Element) currentNode;
+         String tagName = currentElement.getTagName();
+         if(tagName.equals(PARAM_TAG)) {
+            setParameter(currentElement, propSetter);
+         } else if(tagName.equals(APPENDER_REF_TAG)) {
+           eh.setBackupAppender(findAppenderByReference(currentElement));
+         } else if(tagName.equals(LOGGER_REF)) {
+           String loggerName = currentElement.getAttribute(REF_ATTR);      
+           Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
+                : repository.getLogger(loggerName, catFactory);
+           eh.setLogger(logger);
+         } else if(tagName.equals(ROOT_REF)) {
+           Logger root = repository.getRootLogger();
+           eh.setLogger(root);
+         } else {
+          quietParseUnrecognizedElement(eh, currentElement, props);
+      }
+       }
+      }
+      propSetter.activate();
+      appender.setErrorHandler(eh);
+    }
+  }
+  
+  /**
+     Used internally to parse a filter element.
+   */
+  protected
+  void parseFilters(Element element, Appender appender) {
+    String clazz = subst(element.getAttribute(CLASS_ATTR));
+    Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
+                                                Filter.class, null);
+    
+    if(filter != null) {
+      PropertySetter propSetter = new PropertySetter(filter);
+      NodeList children = element.getChildNodes();
+      final int length         = children.getLength();
+
+      for (int loop = 0; loop < length; loop++) {
+       Node currentNode = children.item(loop);
+       if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+         Element currentElement = (Element) currentNode;
+         String tagName = currentElement.getTagName();
+         if(tagName.equals(PARAM_TAG)) {
+            setParameter(currentElement, propSetter);
+         } else {
+            quietParseUnrecognizedElement(filter, currentElement, props);
+      }
+       }
+      }
+      propSetter.activate();
+      LogLog.debug("Adding filter of type ["+filter.getClass()
+                  +"] to appender named ["+appender.getName()+"].");
+      appender.addFilter(filter);
+    }    
+  }
+  
+  /**
+     Used internally to parse an category element.
+  */
+  protected
+  void parseCategory (Element loggerElement) {
+    // Create a new org.apache.log4j.Category object from the <category> element.
+    String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+
+    Logger cat;    
+
+    String className = subst(loggerElement.getAttribute(CLASS_ATTR));
+
+
+    if(EMPTY_STR.equals(className)) {
+      LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
+      cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
+    }
+    else {
+      LogLog.debug("Desired logger sub-class: ["+className+']');
+       try {    
+        Class clazz = Loader.loadClass(className);
+        Method getInstanceMethod = clazz.getMethod("getLogger", 
+                                                   ONE_STRING_PARAM);
+        cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
+       } catch (InvocationTargetException oops) {
+          if (oops.getTargetException() instanceof InterruptedException
+                  || oops.getTargetException() instanceof InterruptedIOException) {
+              Thread.currentThread().interrupt();
+          }
+          LogLog.error("Could not retrieve category ["+catName+
+                     "]. Reported error follows.", oops);
+             return;
+       } catch (Exception oops) {
+             LogLog.error("Could not retrieve category ["+catName+
+                     "]. Reported error follows.", oops);
+             return;
+       }
+    }
+
+    // Setting up a category needs to be an atomic operation, in order
+    // to protect potential log operations while category
+    // configuration is in progress.
+    synchronized(cat) {
+      boolean additivity = OptionConverter.toBoolean(
+                           subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
+                          true);
+    
+      LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
+      cat.setAdditivity(additivity);
+      parseChildrenOfLoggerElement(loggerElement, cat, false);
+    }
+  }
+
+
+  /**
+     Used internally to parse the category factory element.
+  */
+  protected
+  void parseCategoryFactory(Element factoryElement) {
+    String className = subst(factoryElement.getAttribute(CLASS_ATTR));
+
+    if(EMPTY_STR.equals(className)) {
+      LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
+      LogLog.debug("No Category Factory configured.");
+    }
+    else {
+      LogLog.debug("Desired category factory: ["+className+']');
+      Object factory = OptionConverter.instantiateByClassName(className,
+                                                                 LoggerFactory.class, 
+                                                                 null);
+      if (factory instanceof LoggerFactory) {
+          catFactory = (LoggerFactory) factory;
+      } else {
+          LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
+      }
+      PropertySetter propSetter = new PropertySetter(factory);
+
+      Element  currentElement = null;
+      Node     currentNode    = null;
+      NodeList children       = factoryElement.getChildNodes();
+      final int length        = children.getLength();
+
+      for (int loop=0; loop < length; loop++) {
+        currentNode = children.item(loop);
+       if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+         currentElement = (Element)currentNode;
+         if (currentElement.getTagName().equals(PARAM_TAG)) {
+           setParameter(currentElement, propSetter);
+         } else {
+           quietParseUnrecognizedElement(factory, currentElement, props);
+      }
+       }
+      }
+    }
+  }
+
+
+  /**
+     Used internally to parse the roor category element.
+  */
+  protected
+  void parseRoot (Element rootElement) {
+    Logger root = repository.getRootLogger();
+    // category configuration needs to be atomic
+    synchronized(root) {    
+      parseChildrenOfLoggerElement(rootElement, root, true);
+    }
+  }
+
+
+  /**
+     Used internally to parse the children of a category element.
+  */
+  protected
+  void parseChildrenOfLoggerElement(Element catElement,
+                                     Logger cat, boolean isRoot) {
+    
+    PropertySetter propSetter = new PropertySetter(cat);
+    
+    // Remove all existing appenders from cat. They will be
+    // reconstructed if need be.
+    cat.removeAllAppenders();
+
+
+    NodeList children  = catElement.getChildNodes();
+    final int length   = children.getLength();
+    
+    for (int loop = 0; loop < length; loop++) {
+      Node currentNode = children.item(loop);
+
+      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+       Element currentElement = (Element) currentNode;
+       String tagName = currentElement.getTagName();
+       
+       if (tagName.equals(APPENDER_REF_TAG)) {
+         Element appenderRef = (Element) currentNode;
+         Appender appender = findAppenderByReference(appenderRef);
+         String refName =  subst(appenderRef.getAttribute(REF_ATTR));
+         if(appender != null) {
+        LogLog.debug("Adding appender named ["+ refName+ 
+                        "] to category ["+cat.getName()+"].");
+    } else {
+        LogLog.debug("Appender named ["+ refName + "] not found.");
+    }
+           
+         cat.addAppender(appender);
+         
+       } else if(tagName.equals(LEVEL_TAG)) {
+         parseLevel(currentElement, cat, isRoot);      
+       } else if(tagName.equals(PRIORITY_TAG)) {
+         parseLevel(currentElement, cat, isRoot);
+       } else if(tagName.equals(PARAM_TAG)) {
+          setParameter(currentElement, propSetter);
+       } else {
+        quietParseUnrecognizedElement(cat, currentElement, props);
+    }
+      }
+    }
+    propSetter.activate();
+  }
+
+  /**
+     Used internally to parse a layout element.
+  */  
+  protected
+  Layout parseLayout (Element layout_element) {
+    String className = subst(layout_element.getAttribute(CLASS_ATTR));
+    LogLog.debug("Parsing layout of class: \""+className+"\"");                 
+    try {
+      Object instance  = Loader.loadClass(className).newInstance();
+      Layout layout    = (Layout)instance;
+      PropertySetter propSetter = new PropertySetter(layout);
+      
+      NodeList params  = layout_element.getChildNodes();
+      final int length         = params.getLength();
+
+      for (int loop = 0; loop < length; loop++) {
+       Node currentNode = params.item(loop);
+       if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+         Element currentElement = (Element) currentNode;
+         String tagName = currentElement.getTagName();
+         if(tagName.equals(PARAM_TAG)) {
+            setParameter(currentElement, propSetter);
+         } else {
+          parseUnrecognizedElement(instance, currentElement, props);
+      }
+       }
+      }
+      
+      propSetter.activate();
+      return layout;
+    }
+    catch (Exception oops) {
+        if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+            Thread.currentThread().interrupt();
+        }
+      LogLog.error("Could not create the Layout. Reported error follows.",
+                  oops);
+      return null;
+    }
+  }
+
+  protected 
+  void parseRenderer(Element element) {
+    String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
+    String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
+    if(repository instanceof RendererSupport) {
+      RendererMap.addRenderer((RendererSupport) repository, renderedClass, 
+                             renderingClass);
+    }
+  }
+
+    /**
+     * Parses throwable renderer.
+     * @param element throwableRenderer element.
+     * @return configured throwable renderer.
+     * @since 1.2.16.
+     */
+    protected ThrowableRenderer parseThrowableRenderer(final Element element) {
+        String className = subst(element.getAttribute(CLASS_ATTR));
+        LogLog.debug("Parsing throwableRenderer of class: \""+className+"\"");
+        try {
+          Object instance      = Loader.loadClass(className).newInstance();
+          ThrowableRenderer tr         = (ThrowableRenderer)instance;
+          PropertySetter propSetter = new PropertySetter(tr);
+
+          NodeList params      = element.getChildNodes();
+          final int length     = params.getLength();
+
+          for (int loop = 0; loop < length; loop++) {
+                Node currentNode = params.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if(tagName.equals(PARAM_TAG)) {
+                        setParameter(currentElement, propSetter);
+                    } else {
+                        parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+          }
+
+          propSetter.activate();
+          return tr;
+        }
+        catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LogLog.error("Could not create the ThrowableRenderer. Reported error follows.",
+               oops);
+          return null;
+        }
+    }
+
+  /**
+     Used internally to parse a level  element.
+  */
+  protected
+  void parseLevel(Element element, Logger logger, boolean isRoot) {
+    String catName = logger.getName();
+    if(isRoot) {
+      catName = "root";
+    }
+
+    String priStr = subst(element.getAttribute(VALUE_ATTR));
+    LogLog.debug("Level value for "+catName+" is  ["+priStr+"].");
+    
+    if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+      if(isRoot) {
+       LogLog.error("Root level cannot be inherited. Ignoring directive.");
+      } else {
+       logger.setLevel(null);
+      }
+    } else {
+      String className = subst(element.getAttribute(CLASS_ATTR));      
+      if(EMPTY_STR.equals(className)) {        
+       logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
+      } else {
+       LogLog.debug("Desired Level sub-class: ["+className+']');
+       try {    
+         Class clazz = Loader.loadClass(className);
+         Method toLevelMethod = clazz.getMethod("toLevel", 
+                                                   ONE_STRING_PARAM);
+         Level pri = (Level) toLevelMethod.invoke(null, 
+                                                   new Object[] {priStr});
+         logger.setLevel(pri);
+       } catch (Exception oops) {
+        if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+            Thread.currentThread().interrupt();
+        }
+         LogLog.error("Could not create level ["+priStr+
+                      "]. Reported error follows.", oops);
+         return;
+       }
+      }
+    }
+    LogLog.debug(catName + " level set to " + logger.getLevel());    
+  }
+
+  protected
+  void setParameter(Element elem, PropertySetter propSetter) {
+      String name = subst(elem.getAttribute(NAME_ATTR));
+      String value = (elem.getAttribute(VALUE_ATTR));
+      value = subst(OptionConverter.convertSpecialChars(value));
+      propSetter.setProperty(name, value);
+  }
+
+
+  /**
+     Configure log4j using a <code>configuration</code> element as
+     defined in the log4j.dtd. 
+
+  */
+  static
+  public
+  void configure (Element element) {
+    DOMConfigurator configurator = new DOMConfigurator();
+    configurator.doConfigure(element,  LogManager.getLoggerRepository());
+  }
+
+ /**
+     Like {@link #configureAndWatch(String, long)} except that the
+     default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
+     used. 
+
+     @param configFilename A log4j configuration file in XML format.
+
+  */
+  static
+  public
+  void configureAndWatch(String configFilename) {
+    configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
+  }
+
+  /**
+     Read the configuration file <code>configFilename</code> if it
+     exists. Moreover, a thread will be created that will periodically
+     check if <code>configFilename</code> has been created or
+     modified. The period is determined by the <code>delay</code>
+     argument. If a change or file creation is detected, then
+     <code>configFilename</code> is read to configure log4j.  
+
+      @param configFilename A log4j configuration file in XML format.
+      @param delay The delay in milliseconds to wait between each check.
+  */
+  static
+  public
+  void configureAndWatch(String configFilename, long delay) {
+    XMLWatchdog xdog = new XMLWatchdog(configFilename);
+    xdog.setDelay(delay);
+    xdog.start();
+  }
+  
+  private interface ParseAction {
+      Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+  }
+
+
+  public
+  void doConfigure(final String filename, LoggerRepository repository) {
+    ParseAction action = new ParseAction() {
+          public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+              return parser.parse(new File(filename));
+          }
+          public String toString() { 
+              return "file [" + filename + "]"; 
+          }
+    };
+    doConfigure(action, repository);
+  }
+  
+
+  public
+  void doConfigure(final URL url, LoggerRepository repository) {
+      ParseAction action = new ParseAction() {
+          public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+              URLConnection uConn = url.openConnection();
+              uConn.setUseCaches(false);
+              InputStream stream = uConn.getInputStream();
+              try {
+                InputSource src = new InputSource(stream);
+                src.setSystemId(url.toString());
+                return parser.parse(src);
+              } finally {
+                stream.close();
+              }
+          }
+          public String toString() { 
+              return "url [" + url.toString() + "]"; 
+          }
+      };
+      doConfigure(action, repository);
+  }
+
+  /**
+     Configure log4j by reading in a log4j.dtd compliant XML
+     configuration file.
+
+  */
+  public
+  void doConfigure(final InputStream inputStream, LoggerRepository repository) 
+                                          throws FactoryConfigurationError {
+      ParseAction action = new ParseAction() {
+          public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+              InputSource inputSource = new InputSource(inputStream);
+              inputSource.setSystemId("dummy://log4j.dtd");
+              return parser.parse(inputSource);
+          }
+          public String toString() { 
+              return "input stream [" + inputStream.toString() + "]"; 
+          }
+      };
+      doConfigure(action, repository);
+  }
+
+  /**
+     Configure log4j by reading in a log4j.dtd compliant XML
+     configuration file.
+
+  */
+  public
+  void doConfigure(final Reader reader, LoggerRepository repository) 
+                                          throws FactoryConfigurationError {
+      ParseAction action = new ParseAction() {
+          public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+              InputSource inputSource = new InputSource(reader);
+              inputSource.setSystemId("dummy://log4j.dtd");
+              return parser.parse(inputSource);
+          }
+          public String toString() { 
+              return "reader [" + reader.toString() + "]"; 
+          }
+      };
+    doConfigure(action, repository);
+  }
+
+  /**
+     Configure log4j by reading in a log4j.dtd compliant XML
+     configuration file.
+
+  */
+  protected
+  void doConfigure(final InputSource inputSource, LoggerRepository repository) 
+                                          throws FactoryConfigurationError {
+      if (inputSource.getSystemId() == null) {
+          inputSource.setSystemId("dummy://log4j.dtd");
+      }
+      ParseAction action = new ParseAction() {
+          public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+              return parser.parse(inputSource);
+          }
+          public String toString() { 
+              return "input source [" + inputSource.toString() + "]"; 
+          }
+      };
+      doConfigure(action, repository);
+    }
+    
+    
+  private final void doConfigure(final ParseAction action, final LoggerRepository repository)
+         throws FactoryConfigurationError {
+    DocumentBuilderFactory dbf = null;
+    this.repository = repository;
+    try { 
+      LogLog.debug("System property is :"+
+                               OptionConverter.getSystemProperty(dbfKey, 
+                                                                 null)); 
+      dbf = DocumentBuilderFactory.newInstance();
+      LogLog.debug("Standard DocumentBuilderFactory search succeded.");
+      LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
+    } catch(FactoryConfigurationError fce) {
+      Exception e = fce.getException();
+      LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
+      throw fce;
+    }
+      
+    try {
+      dbf.setValidating(true);
+
+      DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+      docBuilder.setErrorHandler(new SAXErrorHandler());      
+      docBuilder.setEntityResolver(new Log4jEntityResolver());
+         
+      Document doc = action.parse(docBuilder);     
+      parse(doc.getDocumentElement());
+    } catch (Exception e) {
+        if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+            Thread.currentThread().interrupt();
+        }
+      // I know this is miserable...
+      LogLog.error("Could not parse "+ action.toString() + ".", e);
+    }
+  }
+
+  /**
+     Configure by taking in an DOM element. 
+  */
+  public void doConfigure(Element element, LoggerRepository repository) {
+    this.repository = repository;
+    parse(element);
+  }
+
+  
+  /**
+     A static version of {@link #doConfigure(String, LoggerRepository)}.  */
+  static
+  public
+  void configure(String filename) throws FactoryConfigurationError {
+    new DOMConfigurator().doConfigure(filename, 
+                                     LogManager.getLoggerRepository());
+  }
+
+  /**
+     A static version of {@link #doConfigure(URL, LoggerRepository)}.
+   */
+  static
+  public
+  void configure(URL url) throws FactoryConfigurationError {
+    new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
+  }
+
+  /**
+     Used internally to configure the log4j framework by parsing a DOM
+     tree of XML elements based on <a
+     href="doc-files/log4j.dtd">log4j.dtd</a>.
+     
+  */
+  protected
+  void parse(Element element) {
+
+    String rootElementName = element.getTagName();
+
+    if (!rootElementName.equals(CONFIGURATION_TAG)) {
+      if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+       LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
+                    "> element has been deprecated.");
+       LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
+      } else {
+       LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
+       return;
+      }
+    }
+
+
+    String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+      
+    LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
+    // if the log4j.dtd is not specified in the XML file, then the
+    // "debug" attribute is returned as the empty string.
+    if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {      
+      LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
+    } else {
+      LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+    }
+
+      //
+      //   reset repository before configuration if reset="true"
+      //       on configuration element.
+      //
+    String resetAttrib = subst(element.getAttribute(RESET_ATTR));
+    LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
+    if(!("".equals(resetAttrib))) {
+         if (OptionConverter.toBoolean(resetAttrib, false)) {
+             repository.resetConfiguration();
+         }
+    }
+
+
+
+    String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+    if(!confDebug.equals("") && !confDebug.equals("null")) {      
+      LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
+      LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
+      LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
+    }
+
+    String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
+    LogLog.debug("Threshold =\"" + thresholdStr +"\".");
+    if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
+      repository.setThreshold(thresholdStr);
+    }
+
+    //Hashtable appenderBag = new Hashtable(11);
+
+    /* Building Appender objects, placing them in a local namespace
+       for future reference */
+
+    // First configure each category factory under the root element.
+    // Category factories need to be configured before any of
+    // categories they support.
+    //
+    String   tagName = null;
+    Element  currentElement = null;
+    Node     currentNode = null;
+    NodeList children = element.getChildNodes();
+    final int length = children.getLength();
+
+    for (int loop = 0; loop < length; loop++) {
+      currentNode = children.item(loop);
+      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+       currentElement = (Element) currentNode;
+       tagName = currentElement.getTagName();
+
+       if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
+         parseCategoryFactory(currentElement);
+       }
+      }
+    }
+    
+    for (int loop = 0; loop < length; loop++) {
+      currentNode = children.item(loop);
+      if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+       currentElement = (Element) currentNode;
+       tagName = currentElement.getTagName();
+
+       if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
+         parseCategory(currentElement);
+       } else if (tagName.equals(ROOT_TAG)) {
+         parseRoot(currentElement);
+       } else if(tagName.equals(RENDERER_TAG)) {
+         parseRenderer(currentElement);
+    } else if(tagName.equals(THROWABLE_RENDERER_TAG)) {
+        if (repository instanceof ThrowableRendererSupport) {
+            ThrowableRenderer tr = parseThrowableRenderer(currentElement);
+            if (tr != null) {
+                ((ThrowableRendererSupport) repository).setThrowableRenderer(tr);
+            }
+        }
+    } else if (!(tagName.equals(APPENDER_TAG)
+            || tagName.equals(CATEGORY_FACTORY_TAG)
+            || tagName.equals(LOGGER_FACTORY_TAG))) {
+        quietParseUnrecognizedElement(repository, currentElement, props);
+    }
+      }
+    }
+  }
+
+  
+  protected
+  String subst(final String value) {
+      return subst(value, props);
+  }
+
+    /**
+     * Substitutes property value for any references in expression.
+     *
+     * @param value value from configuration file, may contain
+     *              literal text, property references or both
+     * @param props properties.
+     * @return evaluated expression, may still contain expressions
+     *         if unable to expand.
+     * @since 1.2.15
+     */
+    public static String subst(final String value, final Properties props) {
+        try {
+            return OptionConverter.substVars(value, props);
+        } catch (IllegalArgumentException e) {
+            LogLog.warn("Could not perform variable substitution.", e);
+            return value;
+        }
+    }
+
+
+    /**
+     * Sets a parameter based from configuration file content.
+     *
+     * @param elem       param element, may not be null.
+     * @param propSetter property setter, may not be null.
+     * @param props      properties
+     * @since 1.2.15
+     */
+    public static void setParameter(final Element elem,
+                                    final PropertySetter propSetter,
+                                    final Properties props) {
+        String name = subst(elem.getAttribute("name"), props);
+        String value = (elem.getAttribute("value"));
+        value = subst(OptionConverter.convertSpecialChars(value), props);
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Creates an object and processes any nested param elements
+     * but does not call activateOptions.  If the class also supports
+     * UnrecognizedElementParser, the parseUnrecognizedElement method
+     * will be call for any child elements other than param.
+     *
+     * @param element       element, may not be null.
+     * @param props         properties
+     * @param expectedClass interface or class expected to be implemented
+     *                      by created class
+     * @return created class or null.
+     * @throws Exception thrown if the contain object should be abandoned.
+     * @since 1.2.15
+     */
+    public static Object parseElement(final Element element,
+                                             final Properties props,
+                                             final Class expectedClass) throws Exception {
+        String clazz = subst(element.getAttribute("class"), props);
+        Object instance = OptionConverter.instantiateByClassName(clazz,
+                expectedClass, null);
+
+        if (instance != null) {
+            PropertySetter propSetter = new PropertySetter(instance);
+            NodeList children = element.getChildNodes();
+            final int length = children.getLength();
+
+            for (int loop = 0; loop < length; loop++) {
+                Node currentNode = children.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if (tagName.equals("param")) {
+                        setParameter(currentElement, propSetter, props);
+                    } else {
+                         parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+            }
+            return instance;
+        }
+        return null;
+    }
+
+}
+
+
+class XMLWatchdog extends FileWatchdog {
+
+    XMLWatchdog(String filename) {
+    super(filename);
+  }
+
+  /**
+     Call {@link DOMConfigurator#configure(String)} with the
+     <code>filename</code> to reconfigure log4j. */
+  public
+  void doOnChange() {
+    new DOMConfigurator().doConfigure(filename, 
+                                     LogManager.getLoggerRepository());
+  }
+}