Hi,

I just created a JDOMConfigurator, allmost a copy
from the original DOMConfigurator, except it uses JDOM of course.

I didn't do it for speed, (haven't checked yet):
1. I had some problems with the original DOMConfigurator.
(Finding the correct Appenders, even if they existed in the
xml file).
2. I like JDOM.

I think where projects already have JDOM this might be usefull.

Just a wild thought, if this class can be usefull, a common interface
XMLConfigurator can be interesting (holding all the static vars at least),
maybe someone has a SAXConfigurator ? (Although JDOM can handle this)

Cheers,

Daniel Bram

----- CUT HERE ------
/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software
 * License version 1.1, a copy of which has been included with this
 * distribution in the LICENSE.APL file.  */

package org.apache.log4j.xml;

import java.lang.reflect.Method;
import org.apache.log4j.Category;
import org.apache.log4j.spi.OptionHandler;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.Configurator;
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.Priority;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.config.PropertySetter;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.FileWatchdog;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.util.*;

import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.*;


/**
  JDOMConfigurator is much the same as DOMConfigurator (original).

  <p>It should behave exactly the same as DOMConfigurator.
  But as you might have guessed it uses JDOM Elements/Documents for
  the configuration.</p>

  <p>Validation is NOT enabled for the moment.  See below getDocument(String
filename)
  or getDocument(String InputStream)</p>

  <p>The DTD is specified in <a
   href="doc-files/log4j.dtd"><b>log4j.dtd</b></a>.

  <p>The main reasons for its existence are:<br>
  1. A problem with DOMConfigurator
  where Appenders were not correctly resolved (those referenced by
appender-ref)
  Might have something to do with the findAppenderByReference function.
  Where the doc.getElementByTags allways returned a zero list even if
appenders
  are in the xml file.<br>
  2. I like JDOM !
  </p>

  @author <a HREF="mailto:[EMAIL PROTECTED]";>Daniel Bram</a>

*/

public class JDOMConfigurator extends BasicConfigurator implements
Configurator {

  static final String CONFIGURATION_TAG = "log4j:configuration";
  static final String OLD_CONFIGURATION_TAG = "configuration";
  static final String RENDERER_TAG      = "renderer";
  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 NAME_ATTR         = "name";
  static final String CLASS_ATTR        = "class";
  static final String VALUE_ATTR        = "value";
  static final String ROOT_TAG          = "root";
  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 DISABLE_OVERRIDE_ATTR = "disableOverride";
  static final String DISABLE_ATTR       = "disable";
  static final String CONFIG_DEBUG_ATTR  = "configDebug";
  static final String INTERNAL_DEBUG_ATTR  = "debug";
  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};


  // key: appenderName, value: appender
  Hashtable appenderBag;

  Properties props;


  /**
   * no argument constructor
   */
  public JDOMConfigurator() {
    appenderBag = new Hashtable();
  }

  /**
     Configure log4j using a <code>configuration</code> element as
     defined in the log4j.dtd.

  */
  static
  public
  void configure (Element element) {
    JDOMConfigurator configurator = new JDOMConfigurator();
    configurator.parse(element, Category.getDefaultHierarchy());
  }

 /**
     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();
  }



  public
  void doConfigure(URL url, Hierarchy hierarchy) {
    try {
      doConfigure(url.openStream(), hierarchy);
    } catch(IOException e) {
      LogLog.error("Could not open ["+url+"].", e);
    }
  }

  /**
     Configure log4j by reading in a log4j.dtd compliant XML
     configuration file.
     @todo change the exception type...
  */
  public
  void doConfigure(InputStream inputStream, Hierarchy hierarchy)
                                          throws IOException {

    Document doc = null;

    try {
      doc = getDocument(inputStream);
      parse(doc.getRootElement(), hierarchy);
    } catch (Exception e) {
      LogLog.error("Could not parse input stream ["+inputStream+"].", e);
    } finally {

    }
  }

  public
  void doConfigure(String filename, Hierarchy hierarchy) {
    Document doc = null;

    try {
      doc = getDocument(filename);
      parse(doc.getRootElement(), hierarchy);
    } catch (Exception e) {
      LogLog.error("Could not open ["+filename+"].", e);
    } finally {

    }
  }


  /**
     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, Hierarchy hierarchy) {

    String rootElementName = element.getName();

    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.getAttributeValue(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.");


    String confDebug = subst(element.getAttributeValue(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 override =
subst(element.getAttributeValue(DISABLE_OVERRIDE_ATTR));
    LogLog.debug("Disable override=\"" + override +"\".");
    // if the log4j.dtd is not specified in the XML file, then the
    // DISABLE_OVERRIDE attribute is returned as the empty string when
    // it is not specified in the XML file.
    if(!override.equals("") && !override.equals("null")) {
      hierarchy.overrideAsNeeded(override);
    }

    String disableStr = subst(element.getAttributeValue(DISABLE_ATTR));
    LogLog.debug("Disable =\"" + disableStr +"\".");
    if(!"".equals(disableStr) && !"null".equals(disableStr)) {
      hierarchy.disable(disableStr);
    }

    //Hashtable appenderBag = new Hashtable(11);

    /* Building Appender objects, placing them in a local namespace
       for future reference */
    //only fetch elements, so if is removed from for loop
    List children = element.getChildren();
//    NodeList children = element.getChildNodes();
    final int length = children.size();

    for (Iterator it = children.iterator(); it.hasNext(); ) {
      Element currentElement = (Element)it.next();
      String tagName = currentElement.getName();
      if (CATEGORY.equals(tagName)) {
        parseCategory(currentElement, hierarchy);
      } else if (ROOT_TAG.equals(tagName)) {
        parseRoot(currentElement, hierarchy);
      } else if (RENDERER_TAG.equals(tagName)) {
        parseRenderer(currentElement, hierarchy);
      }
    }
  }

  /**
     Used internally to parse an appender element.
   */
  protected
  Appender parseAppender (Element appenderElement) {
    String className = subst(appenderElement.getAttributeValue(CLASS_ATTR));
    LogLog.debug("Class name: [" + className+']');
    try {
      Object instance   = Class.forName(className).newInstance();
      Appender appender = (Appender)instance;
      PropertySetter propSetter = new PropertySetter(appender);

      appender.setName(subst(appenderElement.getAttributeValue(NAME_ATTR)));

      for (Iterator it = appenderElement.getChildren().iterator();
it.hasNext(); ) {
        Element currentElement = (Element)it.next();

        // Parse appender parameters
        if (PARAM_TAG.equals(currentElement.getName())) {
          setParameter(currentElement, propSetter);
        }
        // Set appender layout
        else if (LAYOUT_TAG.equals(currentElement.getName())) {
          appender.setLayout(parseLayout(currentElement));
        }
        // Add filters
        else if (FILTER_TAG.equals(currentElement.getName())) {
          parseFilters(currentElement, appender);
        }
        else if (ERROR_HANDLER_TAG.equals(currentElement.getName())) {
          parseErrorHandler(currentElement, appender);
        }
        else if (APPENDER_REF_TAG.equals(currentElement.getName())) {
          String refName =
subst(currentElement.getAttributeValue(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.");
          }
        }
      }
      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) {
      LogLog.error("Could not create an Appender. Reported error follows.",
                   oops);
      return null;
    }
  }


  /**
     Used internally to parse an category element.
  */
  protected
  void parseCategory (Element categoryElement, Hierarchy hierarchy) {
    // Create a new org.apache.log4j.Category object from the <category>
element.
    String catName = subst(categoryElement.getAttributeValue(NAME_ATTR));

    Category cat;

    String className = subst(categoryElement.getAttributeValue(CLASS_ATTR));


    if(EMPTY_STR.equals(className)) {
      LogLog.debug("Retreiving an instance of org.apache.log4j.Category.");
      cat = hierarchy.getInstance(catName);
    }
    else {
      LogLog.debug("Desired category sub-class: ["+className+']');
       try {
         Class clazz = Class.forName(className);
         Method getInstanceMethod = clazz.getMethod("getInstance",
                                                    ONE_STRING_PARAM);
         cat = (Category) getInstanceMethod.invoke(null, new Object[] {catName});
       } 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(categoryElement.getAttributeValue(ADDITIVITY_ATTR)),
                           true);

      LogLog.debug("Setting ["+cat.getName()+"] additivity to
["+additivity+"].");
      cat.setAdditivity(additivity);
      parseChildrenOfCategoryElement(categoryElement, cat, false);
    }
  }


  /**
     Used internally to parse a filter element.
   */
  protected
  void parseFilters(Element element, Appender appender) {
    String clazz = subst(element.getAttributeValue(CLASS_ATTR));
    Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
                                                Filter.class, null);

    if(filter != null) {
      PropertySetter propSetter = new PropertySetter(filter);
      for (Iterator it = element.getChildren().iterator(); it.hasNext(); ) {
        Element currentElement = (Element)it.next();
        String tagName = currentElement.getName();
        if(PARAM_TAG.equals(tagName)) {
          setParameter(currentElement, propSetter);
        }
      }
      propSetter.activate();
      appender.addFilter(filter);
    }
  }

  /**
     Used internally to parse appenders by IDREF.
   */
  protected
  Appender findAppenderByReference(Element appenderRef) {
    String appenderName = subst(appenderRef.getAttributeValue(REF_ATTR));

    Appender appender = (Appender) appenderBag.get(appenderName);
    if(appender != null) {
      return appender;
    } else {
      Document doc = appenderRef.getDocument();

      Element element = null;
      element = getElementWithAttribute(doc.getRootElement(), "appender",
"name", appenderName);

      if(element == null) {
        LogLog.error("No appender named ["+appenderName+"] could be found.");
        return null;
      } else {
        appender = parseAppender(element);
        appenderBag.put(appenderName, appender);
        return appender;
      }
    }
  }

  protected
  void parseRenderer(Element element, Hierarchy hierarchy) {
    String renderingClass =
subst(element.getAttributeValue(RENDERING_CLASS_ATTR));
    String renderedClass =
subst(element.getAttributeValue(RENDERED_CLASS_ATTR));
    addRenderer(hierarchy, renderedClass, renderingClass);
  }


  /**
     Used internally to parse the roor category element.
  */
  protected
  void parseRoot (Element rootElement, Hierarchy hierarchy) {
    Category root = hierarchy.getRoot();
    // category configuration needs to be atomic
    synchronized(root) {
      parseChildrenOfCategoryElement(rootElement, root, true);
    }
  }

  /**
     Used internally to parse the children of a category element.
  */
  protected
  void parseChildrenOfCategoryElement(Element catElement,
                                      Category cat, boolean isRoot) {

    PropertySetter propSetter = new PropertySetter(cat);

    // Remove all existing appenders from cat. They will be
    // reconstructed if need be.
    cat.removeAllAppenders();

    for (Iterator it = catElement.getChildren().iterator(); it.hasNext(); )
{
      Element currentElement = (Element)it.next();
      String tagName = currentElement.getName();

      if (APPENDER_REF_TAG.equals(tagName)) {
        //weird thing going on here in original DOMConfigurator ???
        Element appenderRef = (Element) currentElement;
        Appender appender = findAppenderByReference(appenderRef);
        String refName =  subst(appenderRef.getAttributeValue(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(PRIORITY_TAG)) {
        parsePriority(currentElement, cat, isRoot);
      } else if(tagName.equals(PARAM_TAG)) {
        setParameter(currentElement, propSetter);
      }
    }

    propSetter.activate();
  }

  /**
     Used internally to parse a layout element.
  */
  protected
  Layout parseLayout (Element layout_element) {
    String className = subst(layout_element.getAttributeValue(CLASS_ATTR));
    LogLog.debug("Parsing layout of class: \""+className+"\"");
    try {
      Object instance   = Class.forName(className).newInstance();
      Layout layout     = (Layout)instance;
      PropertySetter propSetter = new PropertySetter(layout);

      for (Iterator it = layout_element.getChildren().iterator();
it.hasNext(); ) {
        Element currentElement = (Element) it.next();
        String tagName = currentElement.getName();
        if(tagName.equals(PARAM_TAG)) {
          setParameter(currentElement, propSetter);
        }
      }

      propSetter.activate();
      return layout;
    }
    catch (Exception oops) {
      LogLog.error("Could not create the Layout. 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.getAttributeValue(CLASS_ATTR)),

org.apache.log4j.spi.ErrorHandler.class,
                                       null);

    if(eh != null) {
      PropertySetter propSetter = new PropertySetter(eh);

      for (Iterator it = element.getChildren().iterator(); it.hasNext(); ) {
        Element currentElement = (Element) it.next();
        String tagName = currentElement.getName();
        if(tagName.equals(PARAM_TAG)) {
          setParameter(currentElement, propSetter);
        }
      }
      propSetter.activate();
      appender.setErrorHandler(eh);
    }
  }

  /**
     Used internally to parse a priority  element.
  */
  protected
  void parsePriority(Element element, Category cat, boolean isRoot) {
    String catName = cat.getName();
    if(isRoot) {
      catName = "root";
    }

    String priStr = subst(element.getAttributeValue(VALUE_ATTR));
    LogLog.debug("Priority value for "+catName+" is  ["+priStr+"].");

    if(BasicConfigurator.INHERITED.equals(priStr)) {
      if(isRoot) {
        LogLog.error("Root priority cannot be inherited. Ignoring directive.");
      } else {
        cat.setPriority(null);
      }
    }
    else {
      String className = subst(element.getAttributeValue(CLASS_ATTR));
      if(EMPTY_STR.equals(className)) {

        cat.setPriority(Priority.toPriority(priStr));
      } else {
        LogLog.debug("Desired Priority sub-class: ["+className+']');
        try {
          Class clazz = Class.forName(className);
          Method toPriorityMethod = clazz.getMethod("toPriority",
                                                    ONE_STRING_PARAM);
          Priority pri = (Priority) toPriorityMethod.invoke(null,
                                                    new Object[] {priStr});
          cat.setPriority(pri);
        } catch (Exception oops) {
          LogLog.error("Could not create priority ["+priStr+
                       "]. Reported error follows.", oops);
          return;
        }
      }
    }
    LogLog.debug(catName + " priority set to " + cat.getPriority());
  }

/**
 * util functions
 * note:
 * If I'm not mistaken,
 * DOM seems to return an EMPTY STRING even if the attribute doesn't exists
 * JDOM returns the real NULL value here.
 * So a quick hack to avoid  "value != null &&" everywhere is if value==null
returns EMPTY_STR here.
 */

  protected
  String subst(String value) {
    if (value == null) {
      return EMPTY_STR;
    }
    try {
      return OptionConverter.substVars(value, props);
    } catch(IllegalArgumentException e) {
      LogLog.warn("Could not perform variable substitution.", e);
      return value;
    }
  }


  protected
  void setParameter(Element elem, PropertySetter propSetter) {
    String name = subst(elem.getAttributeValue(NAME_ATTR));
    String value = (elem.getAttributeValue(VALUE_ATTR));
    value = subst(OptionConverter.convertSpecialChars(value));
    propSetter.setProperty(name, value);
  }

  /**
   * Default SAX Driver class to use
   */
  private static final String DEFAULT_SAX_DRIVER_CLASS =
      "org.apache.xerces.parsers.SAXParser";

/**
   * parse a document using a default SAX parser into a JDOM Document
   * @todo change saxbuilder new call with validator and resolve .dtd file
place issues
   */
  public static Document getDocument(String filename) throws IOException,
JDOMException {
    SAXBuilder builder;
    builder = new SAXBuilder(DEFAULT_SAX_DRIVER_CLASS);
    return builder.build(new File(filename));
  }

  public static Document getDocument(InputStream in) throws IOException,
JDOMException {
    SAXBuilder builder;
    builder = new SAXBuilder(DEFAULT_SAX_DRIVER_CLASS);
    return builder.build(in);
  }

/**
   * get an element trought its attribute comparison
   *
   */
  public static Element getElementWithAttribute(Element parent, String
eltName, String attName, String attValue) {
    return getElementWithAttribute(parent, eltName, new Attribute(attName,
attValue));
  }

/**
   * get an element trought its attribute comparison
   *
   */
  public static Element getElementWithAttribute(Element parent, String
eltName, Attribute att) {
    List list = parent.getChildren(eltName);
    Iterator it = list.listIterator();
    while (it.hasNext()) {
      Element cur = (Element)it.next();
      String value = cur.getAttributeValue(att.getName());
      if (value != null && value.equalsIgnoreCase(att.getValue())) {
        return cur;
      }
    }
    return null;
  }

}


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to