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]