1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.xml;
19
20 import org.apache.log4j.Appender;
21 import org.apache.log4j.Layout;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.LogManager;
24 import org.apache.log4j.Logger;
25 import org.apache.log4j.config.PropertySetter;
26 import org.apache.log4j.helpers.FileWatchdog;
27 import org.apache.log4j.helpers.Loader;
28 import org.apache.log4j.helpers.LogLog;
29 import org.apache.log4j.helpers.OptionConverter;
30 import org.apache.log4j.or.RendererMap;
31 import org.apache.log4j.spi.AppenderAttachable;
32 import org.apache.log4j.spi.Configurator;
33 import org.apache.log4j.spi.ErrorHandler;
34 import org.apache.log4j.spi.Filter;
35 import org.apache.log4j.spi.LoggerFactory;
36 import org.apache.log4j.spi.LoggerRepository;
37 import org.apache.log4j.spi.OptionHandler;
38 import org.apache.log4j.spi.RendererSupport;
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.NamedNodeMap;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44 import org.xml.sax.InputSource;
45 import org.xml.sax.SAXException;
46
47 import javax.xml.parsers.DocumentBuilder;
48 import javax.xml.parsers.DocumentBuilderFactory;
49 import javax.xml.parsers.FactoryConfigurationError;
50 import java.io.File;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.Reader;
54 import java.lang.reflect.Method;
55 import java.net.URL;
56 import java.util.Hashtable;
57 import java.util.Properties;
58
59
60
61
62 /***
63 Use this class to initialize the log4j environment using a DOM tree.
64
65 <p>The DTD is specified in <a
66 href="log4j.dtd"><b>log4j.dtd</b></a>.
67
68 <p>Sometimes it is useful to see how log4j is reading configuration
69 files. You can enable log4j internal logging by defining the
70 <b>log4j.debug</b> variable on the java command
71 line. Alternatively, set the <code>debug</code> attribute in the
72 <code>log4j:configuration</code> element. As in
73 <pre>
74 <log4j:configuration <b>debug="true"</b> xmlns:log4j="http://jakarta.apache.org/log4j/">
75 ...
76 </log4j:configuration>
77 </pre>
78
79 <p>There are sample XML files included in the package.
80
81 @author Christopher Taylor
82 @author Ceki Gülcü
83 @author Anders Kristensen
84
85 @since 0.8.3 */
86 public class DOMConfigurator implements Configurator {
87
88 static final String CONFIGURATION_TAG = "log4j:configuration";
89 static final String OLD_CONFIGURATION_TAG = "configuration";
90 static final String RENDERER_TAG = "renderer";
91 static final String APPENDER_TAG = "appender";
92 static final String APPENDER_REF_TAG = "appender-ref";
93 static final String PARAM_TAG = "param";
94 static final String LAYOUT_TAG = "layout";
95 static final String CATEGORY = "category";
96 static final String LOGGER = "logger";
97 static final String LOGGER_REF = "logger-ref";
98 static final String CATEGORY_FACTORY_TAG = "categoryFactory";
99 static final String LOGGER_FACTORY_TAG = "loggerFactory";
100 static final String NAME_ATTR = "name";
101 static final String CLASS_ATTR = "class";
102 static final String VALUE_ATTR = "value";
103 static final String ROOT_TAG = "root";
104 static final String ROOT_REF = "root-ref";
105 static final String LEVEL_TAG = "level";
106 static final String PRIORITY_TAG = "priority";
107 static final String FILTER_TAG = "filter";
108 static final String ERROR_HANDLER_TAG = "errorHandler";
109 static final String REF_ATTR = "ref";
110 static final String ADDITIVITY_ATTR = "additivity";
111 static final String THRESHOLD_ATTR = "threshold";
112 static final String CONFIG_DEBUG_ATTR = "configDebug";
113 static final String INTERNAL_DEBUG_ATTR = "debug";
114 private static final String RESET_ATTR = "reset";
115 static final String RENDERING_CLASS_ATTR = "renderingClass";
116 static final String RENDERED_CLASS_ATTR = "renderedClass";
117
118 static final String EMPTY_STR = "";
119 static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
120
121 final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
122
123
124
125 Hashtable appenderBag;
126
127 Properties props;
128 LoggerRepository repository;
129
130 protected LoggerFactory catFactory = null;
131
132 /***
133 No argument constructor.
134 */
135 public
136 DOMConfigurator () {
137 appenderBag = new Hashtable();
138 }
139
140 /***
141 Used internally to parse appenders by IDREF name.
142 */
143 protected
144 Appender findAppenderByName(Document doc, String appenderName) {
145 Appender appender = (Appender) appenderBag.get(appenderName);
146
147 if(appender != null) {
148 return appender;
149 } else {
150
151
152
153
154 Element element = null;
155 NodeList list = doc.getElementsByTagName("appender");
156 for (int t=0; t < list.getLength(); t++) {
157 Node node = list.item(t);
158 NamedNodeMap map= node.getAttributes();
159 Node attrNode = map.getNamedItem("name");
160 if (appenderName.equals(attrNode.getNodeValue())) {
161 element = (Element) node;
162 break;
163 }
164 }
165
166
167 if(element == null) {
168 LogLog.error("No appender named ["+appenderName+"] could be found.");
169 return null;
170 } else {
171 appender = parseAppender(element);
172 appenderBag.put(appenderName, appender);
173 return appender;
174 }
175 }
176 }
177 /***
178 Used internally to parse appenders by IDREF element.
179 */
180 protected
181 Appender findAppenderByReference(Element appenderRef) {
182 String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
183 Document doc = appenderRef.getOwnerDocument();
184 return findAppenderByName(doc, appenderName);
185 }
186
187 /***
188 * Delegates unrecognized content to created instance if
189 * it supports UnrecognizedElementParser.
190 * @since 1.2.15
191 * @param instance instance, may be null.
192 * @param element element, may not be null.
193 * @param props properties
194 * @throws IOException thrown if configuration of owner object
195 * should be abandoned.
196 */
197 private static void parseUnrecognizedElement(final Object instance,
198 final Element element,
199 final Properties props) throws Exception {
200 boolean recognized = false;
201 if (instance instanceof UnrecognizedElementHandler) {
202 recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
203 element, props);
204 }
205 if (!recognized) {
206 LogLog.warn("Unrecognized element " + element.getNodeName());
207 }
208 }
209
210 /***
211 * Delegates unrecognized content to created instance if
212 * it supports UnrecognizedElementParser and catches and
213 * logs any exception.
214 * @since 1.2.15
215 * @param instance instance, may be null.
216 * @param element element, may not be null.
217 * @param props properties
218 */
219 private static void quietParseUnrecognizedElement(final Object instance,
220 final Element element,
221 final Properties props) {
222 try {
223 parseUnrecognizedElement(instance, element, props);
224 } catch (Exception ex) {
225 LogLog.error("Error in extension content: ", ex);
226 }
227 }
228
229 /***
230 Used internally to parse an appender element.
231 */
232 protected
233 Appender parseAppender (Element appenderElement) {
234 String className = subst(appenderElement.getAttribute(CLASS_ATTR));
235 LogLog.debug("Class name: [" + className+']');
236 try {
237 Object instance = Loader.loadClass(className).newInstance();
238 Appender appender = (Appender)instance;
239 PropertySetter propSetter = new PropertySetter(appender);
240
241 appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
242
243 NodeList children = appenderElement.getChildNodes();
244 final int length = children.getLength();
245
246 for (int loop = 0; loop < length; loop++) {
247 Node currentNode = children.item(loop);
248
249
250 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
251 Element currentElement = (Element)currentNode;
252
253
254 if (currentElement.getTagName().equals(PARAM_TAG)) {
255 setParameter(currentElement, propSetter);
256 }
257
258 else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
259 appender.setLayout(parseLayout(currentElement));
260 }
261
262 else if (currentElement.getTagName().equals(FILTER_TAG)) {
263 parseFilters(currentElement, appender);
264 }
265 else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
266 parseErrorHandler(currentElement, appender);
267 }
268 else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
269 String refName = subst(currentElement.getAttribute(REF_ATTR));
270 if(appender instanceof AppenderAttachable) {
271 AppenderAttachable aa = (AppenderAttachable) appender;
272 LogLog.debug("Attaching appender named ["+ refName+
273 "] to appender named ["+ appender.getName()+"].");
274 aa.addAppender(findAppenderByReference(currentElement));
275 } else {
276 LogLog.error("Requesting attachment of appender named ["+
277 refName+ "] to appender named ["+ appender.getName()+
278 "] which does not implement org.apache.log4j.spi.AppenderAttachable.");
279 }
280 } else {
281 parseUnrecognizedElement(instance, currentElement, props);
282 }
283 }
284 }
285 propSetter.activate();
286 return appender;
287 }
288
289
290 catch (Exception oops) {
291 LogLog.error("Could not create an Appender. Reported error follows.",
292 oops);
293 return null;
294 }
295 }
296
297 /***
298 Used internally to parse an {@link ErrorHandler} element.
299 */
300 protected
301 void parseErrorHandler(Element element, Appender appender) {
302 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
303 subst(element.getAttribute(CLASS_ATTR)),
304 org.apache.log4j.spi.ErrorHandler.class,
305 null);
306
307 if(eh != null) {
308 eh.setAppender(appender);
309
310 PropertySetter propSetter = new PropertySetter(eh);
311 NodeList children = element.getChildNodes();
312 final int length = children.getLength();
313
314 for (int loop = 0; loop < length; loop++) {
315 Node currentNode = children.item(loop);
316 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
317 Element currentElement = (Element) currentNode;
318 String tagName = currentElement.getTagName();
319 if(tagName.equals(PARAM_TAG)) {
320 setParameter(currentElement, propSetter);
321 } else if(tagName.equals(APPENDER_REF_TAG)) {
322 eh.setBackupAppender(findAppenderByReference(currentElement));
323 } else if(tagName.equals(LOGGER_REF)) {
324 String loggerName = currentElement.getAttribute(REF_ATTR);
325 Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
326 : repository.getLogger(loggerName, catFactory);
327 eh.setLogger(logger);
328 } else if(tagName.equals(ROOT_REF)) {
329 Logger root = repository.getRootLogger();
330 eh.setLogger(root);
331 } else {
332 quietParseUnrecognizedElement(eh, currentElement, props);
333 }
334 }
335 }
336 propSetter.activate();
337 appender.setErrorHandler(eh);
338 }
339 }
340
341 /***
342 Used internally to parse a filter element.
343 */
344 protected
345 void parseFilters(Element element, Appender appender) {
346 String clazz = subst(element.getAttribute(CLASS_ATTR));
347 Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
348 Filter.class, null);
349
350 if(filter != null) {
351 PropertySetter propSetter = new PropertySetter(filter);
352 NodeList children = element.getChildNodes();
353 final int length = children.getLength();
354
355 for (int loop = 0; loop < length; loop++) {
356 Node currentNode = children.item(loop);
357 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
358 Element currentElement = (Element) currentNode;
359 String tagName = currentElement.getTagName();
360 if(tagName.equals(PARAM_TAG)) {
361 setParameter(currentElement, propSetter);
362 } else {
363 quietParseUnrecognizedElement(filter, currentElement, props);
364 }
365 }
366 }
367 propSetter.activate();
368 LogLog.debug("Adding filter of type ["+filter.getClass()
369 +"] to appender named ["+appender.getName()+"].");
370 appender.addFilter(filter);
371 }
372 }
373
374 /***
375 Used internally to parse an category element.
376 */
377 protected
378 void parseCategory (Element loggerElement) {
379
380 String catName = subst(loggerElement.getAttribute(NAME_ATTR));
381
382 Logger cat;
383
384 String className = subst(loggerElement.getAttribute(CLASS_ATTR));
385
386
387 if(EMPTY_STR.equals(className)) {
388 LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
389 cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
390 }
391 else {
392 LogLog.debug("Desired logger sub-class: ["+className+']');
393 try {
394 Class clazz = Loader.loadClass(className);
395 Method getInstanceMethod = clazz.getMethod("getLogger",
396 ONE_STRING_PARAM);
397 cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
398 } catch (Exception oops) {
399 LogLog.error("Could not retrieve category ["+catName+
400 "]. Reported error follows.", oops);
401 return;
402 }
403 }
404
405
406
407
408 synchronized(cat) {
409 boolean additivity = OptionConverter.toBoolean(
410 subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
411 true);
412
413 LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
414 cat.setAdditivity(additivity);
415 parseChildrenOfLoggerElement(loggerElement, cat, false);
416 }
417 }
418
419
420 /***
421 Used internally to parse the category factory element.
422 */
423 protected
424 void parseCategoryFactory(Element factoryElement) {
425 String className = subst(factoryElement.getAttribute(CLASS_ATTR));
426
427 if(EMPTY_STR.equals(className)) {
428 LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
429 LogLog.debug("No Category Factory configured.");
430 }
431 else {
432 LogLog.debug("Desired category factory: ["+className+']');
433 Object factory = OptionConverter.instantiateByClassName(className,
434 LoggerFactory.class,
435 null);
436 if (factory instanceof LoggerFactory) {
437 catFactory = (LoggerFactory) factory;
438 } else {
439 LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
440 }
441 PropertySetter propSetter = new PropertySetter(factory);
442
443 Element currentElement = null;
444 Node currentNode = null;
445 NodeList children = factoryElement.getChildNodes();
446 final int length = children.getLength();
447
448 for (int loop=0; loop < length; loop++) {
449 currentNode = children.item(loop);
450 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
451 currentElement = (Element)currentNode;
452 if (currentElement.getTagName().equals(PARAM_TAG)) {
453 setParameter(currentElement, propSetter);
454 } else {
455 quietParseUnrecognizedElement(factory, currentElement, props);
456 }
457 }
458 }
459 }
460 }
461
462
463 /***
464 Used internally to parse the roor category element.
465 */
466 protected
467 void parseRoot (Element rootElement) {
468 Logger root = repository.getRootLogger();
469
470 synchronized(root) {
471 parseChildrenOfLoggerElement(rootElement, root, true);
472 }
473 }
474
475
476 /***
477 Used internally to parse the children of a category element.
478 */
479 protected
480 void parseChildrenOfLoggerElement(Element catElement,
481 Logger cat, boolean isRoot) {
482
483 PropertySetter propSetter = new PropertySetter(cat);
484
485
486
487 cat.removeAllAppenders();
488
489
490 NodeList children = catElement.getChildNodes();
491 final int length = children.getLength();
492
493 for (int loop = 0; loop < length; loop++) {
494 Node currentNode = children.item(loop);
495
496 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
497 Element currentElement = (Element) currentNode;
498 String tagName = currentElement.getTagName();
499
500 if (tagName.equals(APPENDER_REF_TAG)) {
501 Element appenderRef = (Element) currentNode;
502 Appender appender = findAppenderByReference(appenderRef);
503 String refName = subst(appenderRef.getAttribute(REF_ATTR));
504 if(appender != null)
505 LogLog.debug("Adding appender named ["+ refName+
506 "] to category ["+cat.getName()+"].");
507 else
508 LogLog.debug("Appender named ["+ refName + "] not found.");
509
510 cat.addAppender(appender);
511
512 } else if(tagName.equals(LEVEL_TAG)) {
513 parseLevel(currentElement, cat, isRoot);
514 } else if(tagName.equals(PRIORITY_TAG)) {
515 parseLevel(currentElement, cat, isRoot);
516 } else if(tagName.equals(PARAM_TAG)) {
517 setParameter(currentElement, propSetter);
518 } else {
519 quietParseUnrecognizedElement(cat, currentElement, props);
520 }
521 }
522 }
523 propSetter.activate();
524 }
525
526 /***
527 Used internally to parse a layout element.
528 */
529 protected
530 Layout parseLayout (Element layout_element) {
531 String className = subst(layout_element.getAttribute(CLASS_ATTR));
532 LogLog.debug("Parsing layout of class: \""+className+"\"");
533 try {
534 Object instance = Loader.loadClass(className).newInstance();
535 Layout layout = (Layout)instance;
536 PropertySetter propSetter = new PropertySetter(layout);
537
538 NodeList params = layout_element.getChildNodes();
539 final int length = params.getLength();
540
541 for (int loop = 0; loop < length; loop++) {
542 Node currentNode = (Node)params.item(loop);
543 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
544 Element currentElement = (Element) currentNode;
545 String tagName = currentElement.getTagName();
546 if(tagName.equals(PARAM_TAG)) {
547 setParameter(currentElement, propSetter);
548 } else {
549 parseUnrecognizedElement(instance, currentElement, props);
550 }
551 }
552 }
553
554 propSetter.activate();
555 return layout;
556 }
557 catch (Exception oops) {
558 LogLog.error("Could not create the Layout. Reported error follows.",
559 oops);
560 return null;
561 }
562 }
563
564 protected
565 void parseRenderer(Element element) {
566 String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
567 String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
568 if(repository instanceof RendererSupport) {
569 RendererMap.addRenderer((RendererSupport) repository, renderedClass,
570 renderingClass);
571 }
572 }
573
574 /***
575 Used internally to parse a level element.
576 */
577 protected
578 void parseLevel(Element element, Logger logger, boolean isRoot) {
579 String catName = logger.getName();
580 if(isRoot) {
581 catName = "root";
582 }
583
584 String priStr = subst(element.getAttribute(VALUE_ATTR));
585 LogLog.debug("Level value for "+catName+" is ["+priStr+"].");
586
587 if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
588 if(isRoot) {
589 LogLog.error("Root level cannot be inherited. Ignoring directive.");
590 } else {
591 logger.setLevel(null);
592 }
593 } else {
594 String className = subst(element.getAttribute(CLASS_ATTR));
595 if(EMPTY_STR.equals(className)) {
596 logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
597 } else {
598 LogLog.debug("Desired Level sub-class: ["+className+']');
599 try {
600 Class clazz = Loader.loadClass(className);
601 Method toLevelMethod = clazz.getMethod("toLevel",
602 ONE_STRING_PARAM);
603 Level pri = (Level) toLevelMethod.invoke(null,
604 new Object[] {priStr});
605 logger.setLevel(pri);
606 } catch (Exception oops) {
607 LogLog.error("Could not create level ["+priStr+
608 "]. Reported error follows.", oops);
609 return;
610 }
611 }
612 }
613 LogLog.debug(catName + " level set to " + logger.getLevel());
614 }
615
616 protected
617 void setParameter(Element elem, PropertySetter propSetter) {
618 setParameter(elem, propSetter, props);
619 }
620
621
622 /***
623 Configure log4j using a <code>configuration</code> element as
624 defined in the log4j.dtd.
625
626 */
627 static
628 public
629 void configure (Element element) {
630 DOMConfigurator configurator = new DOMConfigurator();
631 configurator.doConfigure(element, LogManager.getLoggerRepository());
632 }
633
634 /***
635 Like {@link #configureAndWatch(String, long)} except that the
636 default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
637 used.
638
639 @param configFilename A log4j configuration file in XML format.
640
641 */
642 static
643 public
644 void configureAndWatch(String configFilename) {
645 configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
646 }
647
648 /***
649 Read the configuration file <code>configFilename</code> if it
650 exists. Moreover, a thread will be created that will periodically
651 check if <code>configFilename</code> has been created or
652 modified. The period is determined by the <code>delay</code>
653 argument. If a change or file creation is detected, then
654 <code>configFilename</code> is read to configure log4j.
655
656 @param configFilename A log4j configuration file in XML format.
657 @param delay The delay in milliseconds to wait between each check.
658 */
659 static
660 public
661 void configureAndWatch(String configFilename, long delay) {
662 XMLWatchdog xdog = new XMLWatchdog(configFilename);
663 xdog.setDelay(delay);
664 xdog.start();
665 }
666
667 private interface ParseAction {
668 Document parse(final DocumentBuilder parser) throws SAXException, IOException;
669 }
670
671
672 public
673 void doConfigure(final String filename, LoggerRepository repository) {
674 ParseAction action = new ParseAction() {
675 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
676 return parser.parse(new File(filename));
677 }
678 public String toString() {
679 return "file [" + filename + "]";
680 }
681 };
682 doConfigure(action, repository);
683 }
684
685
686 public
687 void doConfigure(final URL url, LoggerRepository repository) {
688 ParseAction action = new ParseAction() {
689 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
690 return parser.parse(url.toString());
691 }
692 public String toString() {
693 return "url [" + url.toString() + "]";
694 }
695 };
696 doConfigure(action, repository);
697 }
698
699 /***
700 Configure log4j by reading in a log4j.dtd compliant XML
701 configuration file.
702
703 */
704 public
705 void doConfigure(final InputStream inputStream, LoggerRepository repository)
706 throws FactoryConfigurationError {
707 ParseAction action = new ParseAction() {
708 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
709 InputSource inputSource = new InputSource(inputStream);
710 inputSource.setSystemId("dummy://log4j.dtd");
711 return parser.parse(inputSource);
712 }
713 public String toString() {
714 return "input stream [" + inputStream.toString() + "]";
715 }
716 };
717 doConfigure(action, repository);
718 }
719
720 /***
721 Configure log4j by reading in a log4j.dtd compliant XML
722 configuration file.
723
724 */
725 public
726 void doConfigure(final Reader reader, LoggerRepository repository)
727 throws FactoryConfigurationError {
728 ParseAction action = new ParseAction() {
729 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
730 InputSource inputSource = new InputSource(reader);
731 inputSource.setSystemId("dummy://log4j.dtd");
732 return parser.parse(inputSource);
733 }
734 public String toString() {
735 return "reader [" + reader.toString() + "]";
736 }
737 };
738 doConfigure(action, repository);
739 }
740
741 /***
742 Configure log4j by reading in a log4j.dtd compliant XML
743 configuration file.
744
745 */
746 protected
747 void doConfigure(final InputSource inputSource, LoggerRepository repository)
748 throws FactoryConfigurationError {
749 if (inputSource.getSystemId() == null) {
750 inputSource.setSystemId("dummy://log4j.dtd");
751 }
752 ParseAction action = new ParseAction() {
753 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
754 return parser.parse(inputSource);
755 }
756 public String toString() {
757 return "input source [" + inputSource.toString() + "]";
758 }
759 };
760 doConfigure(action, repository);
761 }
762
763
764 private final void doConfigure(final ParseAction action, final LoggerRepository repository)
765 throws FactoryConfigurationError {
766 DocumentBuilderFactory dbf = null;
767 this.repository = repository;
768 try {
769 LogLog.debug("System property is :"+
770 OptionConverter.getSystemProperty(dbfKey,
771 null));
772 dbf = DocumentBuilderFactory.newInstance();
773 LogLog.debug("Standard DocumentBuilderFactory search succeded.");
774 LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
775 } catch(FactoryConfigurationError fce) {
776 Exception e = fce.getException();
777 LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
778 throw fce;
779 }
780
781 try {
782 dbf.setValidating(true);
783
784 DocumentBuilder docBuilder = dbf.newDocumentBuilder();
785
786 docBuilder.setErrorHandler(new SAXErrorHandler());
787 docBuilder.setEntityResolver(new Log4jEntityResolver());
788
789 Document doc = action.parse(docBuilder);
790 parse(doc.getDocumentElement());
791 } catch (Exception e) {
792
793 LogLog.error("Could not parse "+ action.toString() + ".", e);
794 }
795 }
796
797 /***
798 Configure by taking in an DOM element.
799 */
800 public void doConfigure(Element element, LoggerRepository repository) {
801 this.repository = repository;
802 parse(element);
803 }
804
805
806 /***
807 A static version of {@link #doConfigure(String, LoggerRepository)}. */
808 static
809 public
810 void configure(String filename) throws FactoryConfigurationError {
811 new DOMConfigurator().doConfigure(filename,
812 LogManager.getLoggerRepository());
813 }
814
815 /***
816 A static version of {@link #doConfigure(URL, LoggerRepository)}.
817 */
818 static
819 public
820 void configure(URL url) throws FactoryConfigurationError {
821 new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
822 }
823
824 /***
825 Used internally to configure the log4j framework by parsing a DOM
826 tree of XML elements based on <a
827 href="doc-files/log4j.dtd">log4j.dtd</a>.
828
829 */
830 protected
831 void parse(Element element) {
832
833 String rootElementName = element.getTagName();
834
835 if (!rootElementName.equals(CONFIGURATION_TAG)) {
836 if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
837 LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
838 "> element has been deprecated.");
839 LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
840 } else {
841 LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
842 return;
843 }
844 }
845
846
847 String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
848
849 LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
850
851
852 if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {
853 LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
854 } else {
855 LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
856 }
857
858
859
860
861
862 String resetAttrib = subst(element.getAttribute(RESET_ATTR));
863 LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
864 if(!("".equals(resetAttrib))) {
865 if (OptionConverter.toBoolean(resetAttrib, false)) {
866 repository.resetConfiguration();
867 }
868 }
869
870
871
872 String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
873 if(!confDebug.equals("") && !confDebug.equals("null")) {
874 LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
875 LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
876 LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
877 }
878
879 String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
880 LogLog.debug("Threshold =\"" + thresholdStr +"\".");
881 if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
882 repository.setThreshold(thresholdStr);
883 }
884
885
886
887
888
889
890
891
892
893
894 String tagName = null;
895 Element currentElement = null;
896 Node currentNode = null;
897 NodeList children = element.getChildNodes();
898 final int length = children.getLength();
899
900 for (int loop = 0; loop < length; loop++) {
901 currentNode = children.item(loop);
902 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
903 currentElement = (Element) currentNode;
904 tagName = currentElement.getTagName();
905
906 if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
907 parseCategoryFactory(currentElement);
908 }
909 }
910 }
911
912 for (int loop = 0; loop < length; loop++) {
913 currentNode = children.item(loop);
914 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
915 currentElement = (Element) currentNode;
916 tagName = currentElement.getTagName();
917
918 if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
919 parseCategory(currentElement);
920 } else if (tagName.equals(ROOT_TAG)) {
921 parseRoot(currentElement);
922 } else if(tagName.equals(RENDERER_TAG)) {
923 parseRenderer(currentElement);
924 } else if (!(tagName.equals(APPENDER_TAG)
925 || tagName.equals(CATEGORY_FACTORY_TAG)
926 || tagName.equals(LOGGER_FACTORY_TAG))) {
927 quietParseUnrecognizedElement(repository, currentElement, props);
928 }
929 }
930 }
931 }
932
933
934 protected
935 String subst(final String value) {
936 return subst(value, props);
937 }
938
939 /***
940 * Substitutes property value for any references in expression.
941 *
942 * @param value value from configuration file, may contain
943 * literal text, property references or both
944 * @param props properties.
945 * @return evaluated expression, may still contain expressions
946 * if unable to expand.
947 * @since 1.2.15
948 */
949 public static String subst(final String value, final Properties props) {
950 try {
951 return OptionConverter.substVars(value, props);
952 } catch (IllegalArgumentException e) {
953 LogLog.warn("Could not perform variable substitution.", e);
954 return value;
955 }
956 }
957
958
959 /***
960 * Sets a parameter based from configuration file content.
961 *
962 * @param elem param element, may not be null.
963 * @param propSetter property setter, may not be null.
964 * @param props properties
965 * @since 1.2.15
966 */
967 public static void setParameter(final Element elem,
968 final PropertySetter propSetter,
969 final Properties props) {
970 String name = subst(elem.getAttribute("name"), props);
971 String value = (elem.getAttribute("value"));
972 value = subst(OptionConverter.convertSpecialChars(value), props);
973 propSetter.setProperty(name, value);
974 }
975
976 /***
977 * Creates an object and processes any nested param elements
978 * but does not call activateOptions. If the class also supports
979 * UnrecognizedElementParser, the parseUnrecognizedElement method
980 * will be call for any child elements other than param.
981 *
982 * @param element element, may not be null.
983 * @param props properties
984 * @param expectedClass interface or class expected to be implemented
985 * by created class
986 * @return created class or null.
987 * @throws Exception thrown if the contain object should be abandoned.
988 * @since 1.2.15
989 */
990 public static Object parseElement(final Element element,
991 final Properties props,
992 final Class expectedClass) throws Exception {
993 String clazz = subst(element.getAttribute("class"), props);
994 Object instance = OptionConverter.instantiateByClassName(clazz,
995 expectedClass, null);
996
997 if (instance != null) {
998 PropertySetter propSetter = new PropertySetter(instance);
999 NodeList children = element.getChildNodes();
1000 final int length = children.getLength();
1001
1002 for (int loop = 0; loop < length; loop++) {
1003 Node currentNode = children.item(loop);
1004 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
1005 Element currentElement = (Element) currentNode;
1006 String tagName = currentElement.getTagName();
1007 if (tagName.equals("param")) {
1008 setParameter(currentElement, propSetter, props);
1009 } else {
1010 parseUnrecognizedElement(instance, currentElement, props);
1011 }
1012 }
1013 }
1014 return instance;
1015 }
1016 return null;
1017 }
1018
1019 }
1020
1021
1022 class XMLWatchdog extends FileWatchdog {
1023
1024 XMLWatchdog(String filename) {
1025 super(filename);
1026 }
1027
1028 /***
1029 Call {@link DOMConfigurator#configure(String)} with the
1030 <code>filename</code> to reconfigure log4j. */
1031 public
1032 void doOnChange() {
1033 new DOMConfigurator().doConfigure(filename,
1034 LogManager.getLoggerRepository());
1035 }
1036 }